Merge branch '3.0.x'
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.discoveryserver;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnHttpDiscoveryCatalogWatcherEnabled;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnKubernetesCatalogEnabled;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.EndpointNameAndNamespace;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
@RestController
|
||||
@ConditionalOnKubernetesCatalogEnabled
|
||||
@ConditionalOnHttpDiscoveryCatalogWatcherEnabled
|
||||
class DiscoveryCatalogWatcherController {
|
||||
|
||||
private final HeartBeatListener heartBeatListener;
|
||||
|
||||
DiscoveryCatalogWatcherController(HeartBeatListener heartBeatListener) {
|
||||
this.heartBeatListener = heartBeatListener;
|
||||
}
|
||||
|
||||
@GetMapping("/state")
|
||||
Mono<List<EndpointNameAndNamespace>> state() {
|
||||
return Mono.defer(() -> Mono.just(heartBeatListener.lastState().get()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,11 +18,13 @@ package org.springframework.cloud.kubernetes.discoveryserver;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
public class DiscoveryServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.discoveryserver;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnHttpDiscoveryCatalogWatcherEnabled;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnKubernetesCatalogEnabled;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.EndpointNameAndNamespace;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.log.LogAccessor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.CATALOG_WATCHER_DEFAULT_DELAY;
|
||||
import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.CATALOG_WATCH_PROPERTY_NAME;
|
||||
|
||||
/**
|
||||
* Listener for a HeartbeatEvent that comes from KubernetesCatalogWatch.
|
||||
*
|
||||
* @author wind57
|
||||
*/
|
||||
@Component
|
||||
@ConditionalOnKubernetesCatalogEnabled
|
||||
@ConditionalOnHttpDiscoveryCatalogWatcherEnabled
|
||||
class HeartBeatListener implements ApplicationListener<HeartbeatEvent> {
|
||||
|
||||
private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(HeartBeatListener.class));
|
||||
|
||||
private final AtomicReference<List<EndpointNameAndNamespace>> lastState = new AtomicReference<>(List.of());
|
||||
|
||||
HeartBeatListener(Environment environment) {
|
||||
String watchDelay = environment.getProperty(CATALOG_WATCH_PROPERTY_NAME);
|
||||
if (watchDelay != null) {
|
||||
LOG.debug("using delay : " + watchDelay);
|
||||
}
|
||||
else {
|
||||
LOG.debug("using default watch delay : " + CATALOG_WATCHER_DEFAULT_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onApplicationEvent(HeartbeatEvent event) {
|
||||
LOG.debug(() -> "received heartbeat event");
|
||||
List<EndpointNameAndNamespace> state = (List<EndpointNameAndNamespace>) event.getValue();
|
||||
LOG.debug(() -> "state received : " + state);
|
||||
lastState.set(state);
|
||||
}
|
||||
|
||||
AtomicReference<List<EndpointNameAndNamespace>> lastState() {
|
||||
return lastState;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.discoveryserver;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.EndpointNameAndNamespace;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
class DiscoveryCatalogWatcherControllerTests {
|
||||
|
||||
private final HeartBeatListener heartBeatListener = Mockito.mock(HeartBeatListener.class);
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
Mockito.when(heartBeatListener.lastState())
|
||||
.thenReturn(new AtomicReference<>(List.of(new EndpointNameAndNamespace("one", "two"))));
|
||||
|
||||
DiscoveryCatalogWatcherController catalogWatcherController = new DiscoveryCatalogWatcherController(
|
||||
heartBeatListener);
|
||||
|
||||
StepVerifier.create(catalogWatcherController.state())
|
||||
.expectNext(List.of(new EndpointNameAndNamespace("one", "two"))).verifyComplete();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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.discoveryserver;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.cloud.kubernetes.client.discovery.reactive.KubernetesInformerReactiveDiscoveryClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
class DiscoveryServerApplicationContextTests {
|
||||
|
||||
@Nested
|
||||
@SpringBootTest(classes = TestConfig.class,
|
||||
properties = "spring.cloud.kubernetes.http.discovery.catalog.watcher.enabled=true")
|
||||
class BothControllersPresent {
|
||||
|
||||
@Autowired
|
||||
private ObjectProvider<DiscoveryServerController> discoveryServerController;
|
||||
|
||||
@Autowired
|
||||
private ObjectProvider<DiscoveryCatalogWatcherController> discoveryCatalogWatcherController;
|
||||
|
||||
@Autowired
|
||||
private ObjectProvider<HeartBeatListener> heartBeatListener;
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
Assertions.assertNotNull(discoveryServerController.getIfAvailable());
|
||||
Assertions.assertNotNull(discoveryCatalogWatcherController.getIfAvailable());
|
||||
Assertions.assertNotNull(heartBeatListener.getIfAvailable());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
@SpringBootTest(classes = TestConfig.class,
|
||||
properties = { "spring.cloud.kubernetes.discovery.catalog-services-watch.enabled=false",
|
||||
"spring.cloud.kubernetes.http.discovery.catalog.watcher.enabled=true" })
|
||||
class CatalogControllerNotPresentOne {
|
||||
|
||||
@Autowired
|
||||
private ObjectProvider<DiscoveryServerController> discoveryServerController;
|
||||
|
||||
@Autowired
|
||||
private ObjectProvider<DiscoveryCatalogWatcherController> discoveryCatalogWatcherController;
|
||||
|
||||
@Autowired
|
||||
private ObjectProvider<HeartBeatListener> heartBeatListener;
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
Assertions.assertNotNull(discoveryServerController.getIfAvailable());
|
||||
Assertions.assertNull(discoveryCatalogWatcherController.getIfAvailable());
|
||||
Assertions.assertNull(heartBeatListener.getIfAvailable());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
@SpringBootTest(classes = TestConfig.class,
|
||||
properties = { "spring.cloud.kubernetes.discovery.catalog-services-watch.enabled=true",
|
||||
"spring.cloud.kubernetes.http.discovery.catalog.watcher.enabled=false" })
|
||||
class CatalogControllerNotPresentTwo {
|
||||
|
||||
@Autowired
|
||||
private ObjectProvider<DiscoveryServerController> discoveryServerController;
|
||||
|
||||
@Autowired
|
||||
private ObjectProvider<DiscoveryCatalogWatcherController> discoveryCatalogWatcherController;
|
||||
|
||||
@Autowired
|
||||
private ObjectProvider<HeartBeatListener> heartBeatListener;
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
Assertions.assertNotNull(discoveryServerController.getIfAvailable());
|
||||
Assertions.assertNull(discoveryCatalogWatcherController.getIfAvailable());
|
||||
Assertions.assertNull(heartBeatListener.getIfAvailable());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
@SpringBootTest(classes = TestConfig.class,
|
||||
properties = { "spring.cloud.kubernetes.discovery.catalog-services-watch.enabled=false",
|
||||
"spring.cloud.kubernetes.http.discovery.catalog.watcher.enabled=false" })
|
||||
class CatalogControllerNotPresentThree {
|
||||
|
||||
@Autowired
|
||||
private ObjectProvider<DiscoveryServerController> discoveryServerController;
|
||||
|
||||
@Autowired
|
||||
private ObjectProvider<DiscoveryCatalogWatcherController> discoveryCatalogWatcherController;
|
||||
|
||||
@Autowired
|
||||
private ObjectProvider<HeartBeatListener> heartBeatListener;
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
Assertions.assertNotNull(discoveryServerController.getIfAvailable());
|
||||
Assertions.assertNull(discoveryCatalogWatcherController.getIfAvailable());
|
||||
Assertions.assertNull(heartBeatListener.getIfAvailable());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestConfiguration
|
||||
static class TestConfig {
|
||||
|
||||
@Bean
|
||||
KubernetesInformerReactiveDiscoveryClient discoveryClient() {
|
||||
return Mockito.mock(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.discoveryserver;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
|
||||
import org.springframework.cloud.kubernetes.client.discovery.reactive.KubernetesInformerReactiveDiscoveryClient;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.EndpointNameAndNamespace;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
@SpringBootTest(classes = HeartbeatTest.TestConfig.class,
|
||||
properties = "spring.cloud.kubernetes.http.discovery.catalog.watcher.enabled=true")
|
||||
@AutoConfigureWebTestClient
|
||||
class HeartbeatTest {
|
||||
|
||||
@Autowired
|
||||
private WebTestClient client;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
@Test
|
||||
void testHeartbeat() {
|
||||
Assertions.assertTrue(true);
|
||||
client.get().uri("/state").exchange().expectStatus().is2xxSuccessful().expectBody().json("[]");
|
||||
|
||||
context.getBean(HeartbeatPublisher.class).publishEvent();
|
||||
client.get().uri("/state").exchange().expectStatus().is2xxSuccessful().expectBody().json("""
|
||||
[
|
||||
{
|
||||
"endpointName":"endpoint-name",
|
||||
"namespace":"namespaceA"
|
||||
}
|
||||
]
|
||||
""");
|
||||
}
|
||||
|
||||
@TestConfiguration
|
||||
static class TestConfig {
|
||||
|
||||
@Bean
|
||||
KubernetesInformerReactiveDiscoveryClient client() {
|
||||
return Mockito.mock(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
HeartbeatPublisher heartbeatPublisher() {
|
||||
return new HeartbeatPublisher();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class HeartbeatPublisher implements ApplicationEventPublisherAware {
|
||||
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
void publishEvent() {
|
||||
publisher.publishEvent(
|
||||
new HeartbeatEvent("test", List.of(new EndpointNameAndNamespace("endpoint-name", "namespaceA"))));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user