From 71ec62b58e6041b8188ba766aa67ef2ce249ed39 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Tue, 18 Aug 2020 15:37:50 -0400 Subject: [PATCH] Syslog supplier/source - Provide TCP/UDP based syslog supplier and then generate apps based on that --- applications/source/pom.xml | 1 + applications/source/syslog-source/README.adoc | 18 ++ applications/source/syslog-source/pom.xml | 128 +++++++++++++ ...onfiguration-metadata-whitelist.properties | 2 + ...dataflow-configuration-metadata.properties | 2 + .../app/source/syslog/SyslogSourceTests.java | 87 +++++++++ functions/function-dependencies/pom.xml | 5 + functions/pom.xml | 1 + .../supplier/syslog-supplier/README.adoc | 32 ++++ functions/supplier/syslog-supplier/pom.xml | 50 +++++ .../syslog/SyslogSupplierConfiguration.java | 177 ++++++++++++++++++ .../syslog/SyslogSupplierProperties.java | 142 ++++++++++++++ .../syslog/AbstractSyslogSupplierTests.java | 107 +++++++++++ .../cloud/fn/supplier/syslog/NotNioTests.java | 35 ++++ .../syslog/PropertiesPopulatedTests.java | 38 ++++ .../fn/supplier/syslog/Tcp3164Tests.java | 47 +++++ .../fn/supplier/syslog/Tcp5424Tests.java | 49 +++++ .../supplier/syslog/TcpAndUdp3164Tests.java | 52 +++++ .../supplier/syslog/TcpAndUdp5424Tests.java | 53 ++++++ .../fn/supplier/syslog/Udp3164Tests.java | 52 +++++ .../fn/supplier/syslog/Udp5424Tests.java | 47 +++++ 21 files changed, 1125 insertions(+) create mode 100644 applications/source/syslog-source/README.adoc create mode 100644 applications/source/syslog-source/pom.xml create mode 100644 applications/source/syslog-source/src/main/resources/META-INF/dataflow-configuration-metadata-whitelist.properties create mode 100644 applications/source/syslog-source/src/main/resources/META-INF/dataflow-configuration-metadata.properties create mode 100644 applications/source/syslog-source/src/test/java/org/springframework/cloud/stream/app/source/syslog/SyslogSourceTests.java create mode 100644 functions/supplier/syslog-supplier/README.adoc create mode 100644 functions/supplier/syslog-supplier/pom.xml create mode 100644 functions/supplier/syslog-supplier/src/main/java/org/springframework/cloud/fn/supplier/syslog/SyslogSupplierConfiguration.java create mode 100644 functions/supplier/syslog-supplier/src/main/java/org/springframework/cloud/fn/supplier/syslog/SyslogSupplierProperties.java create mode 100644 functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/AbstractSyslogSupplierTests.java create mode 100644 functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/NotNioTests.java create mode 100644 functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/PropertiesPopulatedTests.java create mode 100644 functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/Tcp3164Tests.java create mode 100644 functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/Tcp5424Tests.java create mode 100644 functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/TcpAndUdp3164Tests.java create mode 100644 functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/TcpAndUdp5424Tests.java create mode 100644 functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/Udp3164Tests.java create mode 100644 functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/Udp5424Tests.java diff --git a/applications/source/pom.xml b/applications/source/pom.xml index 38bdcfc0..ab76b0fd 100644 --- a/applications/source/pom.xml +++ b/applications/source/pom.xml @@ -29,5 +29,6 @@ twitter-message-source websocket-source cdc-debezium-source + syslog-source diff --git a/applications/source/syslog-source/README.adoc b/applications/source/syslog-source/README.adoc new file mode 100644 index 00000000..685fb4c9 --- /dev/null +++ b/applications/source/syslog-source/README.adoc @@ -0,0 +1,18 @@ +//tag::ref-doc[] += SYSLOG + +The syslog source receives SYSLOG packets over UDP, TCP, or both. RFC3164 (BSD) and RFC5424 formats are supported. + +== Options + +//tag::configuration-properties[] +$$syslog.supplier.buffer-size$$:: $$the buffer size used when decoding messages; larger messages will be rejected.$$ *($$Integer$$, default: `$$2048$$`)* +$$syslog.supplier.nio$$:: $$whether or not to use NIO (when supporting a large number of connections).$$ *($$Boolean$$, default: `$$false$$`)* +$$syslog.supplier.port$$:: $$The port to listen on.$$ *($$Integer$$, default: `$$1514$$`)* +$$syslog.supplier.protocol$$:: $$Protocol used for SYSLOG (tcp or udp).$$ *($$Protocol$$, default: `$$$$`, possible values: `tcp`,`udp`,`both`)* +$$syslog.supplier.reverse-lookup$$:: $$whether or not to perform a reverse lookup on the incoming socket.$$ *($$Boolean$$, default: `$$false$$`)* +$$syslog.supplier.rfc$$:: $$'5424' or '3164' - the syslog format according the the RFC; 3164 is aka 'BSD' format.$$ *($$String$$, default: `$$3164$$`)* +$$syslog.supplier.socket-timeout$$:: $$the socket timeout.$$ *($$Integer$$, default: `$$0$$`)* +//end::configuration-properties[] + +//end::ref-doc[] diff --git a/applications/source/syslog-source/pom.xml b/applications/source/syslog-source/pom.xml new file mode 100644 index 00000000..2a8e8335 --- /dev/null +++ b/applications/source/syslog-source/pom.xml @@ -0,0 +1,128 @@ + + + 4.0.0 + syslog-source + 3.0.0-SNAPSHOT + syslog-source + syslog source apps + jar + + + org.springframework.cloud.stream.app + stream-applications-core + 3.0.0-SNAPSHOT + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.cloud.fn + syslog-supplier + + + + + + + org.springframework.cloud + spring-cloud-app-starter-doc-maven-plugin + + + org.springframework.cloud.stream.app.plugin + spring-cloud-stream-app-maven-plugin + + + syslog + source + ${project.version} + org.springframework.cloud.fn.supplier.syslog.SyslogSupplierConfiguration.class + + + + org.springframework.cloud.fn + syslog-supplier + + + org.springframework.cloud.stream.app + stream-applications-composite-function-support + ${stream-apps-core.version} + + + + + + + + + + + true + + spring-snapshots + Spring Snapshots + https://repo.spring.io/libs-snapshot-local + + + + false + + spring-milestones + Spring Milestones + https://repo.spring.io/libs-milestone-local + + + + false + + spring-releases + Spring Releases + https://repo.spring.io/release + + + + false + + spring-libs-release + Spring Libs Release + https://repo.spring.io/libs-release + + + + false + + spring-milestone-release + Spring Milestone Release + https://repo.spring.io/libs-milestone + + + + + spring-releases + Spring Releases + https://repo.spring.io/libs-release + + + + true + + spring-snapshots + Spring Snapshots + https://repo.spring.io/libs-snapshot-local + + + + false + + spring-milestones + Spring Milestones + https://repo.spring.io/libs-milestone-local + + + + diff --git a/applications/source/syslog-source/src/main/resources/META-INF/dataflow-configuration-metadata-whitelist.properties b/applications/source/syslog-source/src/main/resources/META-INF/dataflow-configuration-metadata-whitelist.properties new file mode 100644 index 00000000..60491dd5 --- /dev/null +++ b/applications/source/syslog-source/src/main/resources/META-INF/dataflow-configuration-metadata-whitelist.properties @@ -0,0 +1,2 @@ +configuration-properties.classes=org.springframework.cloud.fn.supplier.syslog.SyslogSupplierProperties + diff --git a/applications/source/syslog-source/src/main/resources/META-INF/dataflow-configuration-metadata.properties b/applications/source/syslog-source/src/main/resources/META-INF/dataflow-configuration-metadata.properties new file mode 100644 index 00000000..60491dd5 --- /dev/null +++ b/applications/source/syslog-source/src/main/resources/META-INF/dataflow-configuration-metadata.properties @@ -0,0 +1,2 @@ +configuration-properties.classes=org.springframework.cloud.fn.supplier.syslog.SyslogSupplierProperties + diff --git a/applications/source/syslog-source/src/test/java/org/springframework/cloud/stream/app/source/syslog/SyslogSourceTests.java b/applications/source/syslog-source/src/test/java/org/springframework/cloud/stream/app/source/syslog/SyslogSourceTests.java new file mode 100644 index 00000000..f70e61ca --- /dev/null +++ b/applications/source/syslog-source/src/test/java/org/springframework/cloud/stream/app/source/syslog/SyslogSourceTests.java @@ -0,0 +1,87 @@ +/* + * Copyright 2020-2020 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.stream.app.source.syslog; + +import java.net.Socket; +import java.util.Map; + +import javax.net.SocketFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.cloud.fn.supplier.syslog.SyslogSupplierConfiguration; +import org.springframework.cloud.stream.binder.test.OutputDestination; +import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Import; +import org.springframework.integration.ip.tcp.connection.AbstractServerConnectionFactory; +import org.springframework.messaging.Message; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SyslogSourceTests { + + private static final String RFC3164_PACKET = "<157>JUL 26 22:08:35 WEBERN TESTING[70729]: TEST SYSLOG MESSAGE"; + + @Test + public void testBasicSyslogSourceWithBinder() throws Exception { + + try (ConfigurableApplicationContext context = new SpringApplicationBuilder( + TestChannelBinderConfiguration + .getCompleteConfiguration(SyslogSourceTestApplication.class)) + .web(WebApplicationType.NONE) + .run("--spring.cloud.function.definition=syslogSupplier", "--syslog.supplier.port=0")) { + + AbstractServerConnectionFactory connectionFactory = context.getBean(AbstractServerConnectionFactory.class); + + sendTcp(RFC3164_PACKET + "\n", connectionFactory); + + OutputDestination target = context.getBean(OutputDestination.class); + Message sourceMessage = target.receive(10000); + String actual = new String(sourceMessage.getPayload()); + final Map map = new ObjectMapper().readValue(actual, Map.class); + assertThat(map.get("HOST")).isEqualTo("WEBERN"); + } + } + + private void sendTcp(String syslog, AbstractServerConnectionFactory connectionFactory) throws Exception { + int port = getPort(connectionFactory); + Socket socket = SocketFactory.getDefault().createSocket("localhost", port); + socket.getOutputStream().write(syslog.getBytes()); + socket.close(); + } + + private int getPort(AbstractServerConnectionFactory connectionFactory) throws Exception { + int n = 0; + while (n++ < 100 && !connectionFactory.isListening()) { + Thread.sleep(100); + } + assertThat(connectionFactory.isListening()).isTrue(); + int port = connectionFactory.getPort(); + assertThat(port > 0).isTrue(); + return port; + } + + @SpringBootApplication + @Import(SyslogSupplierConfiguration.class) + public static class SyslogSourceTestApplication { + } +} diff --git a/functions/function-dependencies/pom.xml b/functions/function-dependencies/pom.xml index 6b4a0d6b..cceac754 100644 --- a/functions/function-dependencies/pom.xml +++ b/functions/function-dependencies/pom.xml @@ -15,6 +15,11 @@ + + org.springframework.cloud.fn + syslog-supplier + ${project.version} + org.springframework.cloud.fn cdc-debezium-supplier diff --git a/functions/pom.xml b/functions/pom.xml index b53fcb19..a90e612a 100644 --- a/functions/pom.xml +++ b/functions/pom.xml @@ -101,6 +101,7 @@ supplier/s3-supplier supplier/twitter-supplier supplier/cdc-debezium-supplier + supplier/syslog-supplier spring-functions-parent function-dependencies diff --git a/functions/supplier/syslog-supplier/README.adoc b/functions/supplier/syslog-supplier/README.adoc new file mode 100644 index 00000000..85cd3b0e --- /dev/null +++ b/functions/supplier/syslog-supplier/README.adoc @@ -0,0 +1,32 @@ +# Syslog Supplier + +Syslog supplier that produces both TCP and UDP based syslog events. +The `Supplier` uses the `TcpSyslogReceivingChannelAdapter` and `UdpSyslogReceivingChannelAdapter` from Spring Integration. +This supplier gives you a reactive stream of messages and the supplier has a signature of `Supplier>>`. +Users have to subscribe to this `Flux` and receive the data. + +## Beans for injection + +You can import the `SyslogSupplierConfiguration` in the application and then inject the following bean. + +`syslogSupplier` + +You need to inject this as `Supplier>>`. + +You can use `syslogSupplier` as a qualifier when injecting. + +Once injected, you can use the `get` method of the `Supplier` to invoke it and then subscribe to the returned `Flux`. + +## Configuration Options + +All configuration properties are prefixed with `syslog.supplier`. + +For more information on the various options available, please see link:src/main/java/org/springframework/cloud/fn/supplier/syslog/SyslogSupplierProperties.java[SyslogSupplierProperties]. + +## Tests + +See this link:src/test/java/org/springframework/cloud/fn/supplier/syslog[test suite] for the various ways, this supplier is used. + +## Other usage + +See this https://github.com/spring-cloud/stream-applications/blob/master/applications/source/syslog-source/README.adoc[README] where this supplier is used to create a Spring Cloud Stream application where it makes a Syslog Source. \ No newline at end of file diff --git a/functions/supplier/syslog-supplier/pom.xml b/functions/supplier/syslog-supplier/pom.xml new file mode 100644 index 00000000..f3653f73 --- /dev/null +++ b/functions/supplier/syslog-supplier/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + syslog-supplier + 1.0.0-SNAPSHOT + syslog-supplier + syslog supplier + + + org.springframework.cloud.fn + spring-functions-parent + 1.0.0-SNAPSHOT + ../../spring-functions-parent + + + + + org.springframework.integration + spring-integration-syslog + + + org.springframework.boot + spring-boot-starter-integration + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-configuration-processor + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.integration + spring-integration-test-support + + + io.projectreactor + reactor-test + test + + + + diff --git a/functions/supplier/syslog-supplier/src/main/java/org/springframework/cloud/fn/supplier/syslog/SyslogSupplierConfiguration.java b/functions/supplier/syslog-supplier/src/main/java/org/springframework/cloud/fn/supplier/syslog/SyslogSupplierConfiguration.java new file mode 100644 index 00000000..af81e090 --- /dev/null +++ b/functions/supplier/syslog-supplier/src/main/java/org/springframework/cloud/fn/supplier/syslog/SyslogSupplierConfiguration.java @@ -0,0 +1,177 @@ +/* + * Copyright 2020-2020 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.fn.supplier.syslog; + +import java.util.function.Supplier; + +import reactor.core.publisher.Flux; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.serializer.Deserializer; +import org.springframework.integration.channel.FluxMessageChannel; +import org.springframework.integration.ip.tcp.connection.AbstractServerConnectionFactory; +import org.springframework.integration.ip.tcp.connection.TcpNetServerConnectionFactory; +import org.springframework.integration.ip.tcp.connection.TcpNioServerConnectionFactory; +import org.springframework.integration.ip.tcp.serializer.ByteArrayLfSerializer; +import org.springframework.integration.syslog.DefaultMessageConverter; +import org.springframework.integration.syslog.MessageConverter; +import org.springframework.integration.syslog.RFC5424MessageConverter; +import org.springframework.integration.syslog.inbound.RFC6587SyslogDeserializer; +import org.springframework.integration.syslog.inbound.SyslogReceivingChannelAdapterSupport; +import org.springframework.integration.syslog.inbound.TcpSyslogReceivingChannelAdapter; +import org.springframework.integration.syslog.inbound.UdpSyslogReceivingChannelAdapter; +import org.springframework.messaging.Message; + +/** + * Configuration class for SYSLOG Supplier. + * + * @author Soby Chacko + */ +@Configuration +@EnableConfigurationProperties(SyslogSupplierProperties.class) +public class SyslogSupplierConfiguration { + + @Autowired + private SyslogSupplierProperties properties; + + @Bean + public FluxMessageChannel output() { + return new FluxMessageChannel(); + } + + @Bean + public Supplier>> syslogSupplier(ObjectProvider udpApapterProvider, + ObjectProvider tcpAdapterProvider) { + return () -> Flux.from(output()) + .doOnSubscribe(subscription -> { + final UdpSyslogReceivingChannelAdapter udpAdapter = udpApapterProvider.getIfAvailable(); + final TcpSyslogReceivingChannelAdapter tcpAdapter = tcpAdapterProvider.getIfAvailable(); + if (udpAdapter != null) { + udpAdapter.start(); + } + if (tcpAdapter != null) { + tcpAdapter.start(); + } + }); + } + + @Bean + @ConditionalOnProperty(name = "syslog.supplier.protocol", havingValue = "udp") + public UdpSyslogReceivingChannelAdapter udpAdapter() { + return createUdpAdapter(); + } + + @Bean + @ConditionalOnProperty(name = "syslog.supplier.protocol", havingValue = "both") + public UdpSyslogReceivingChannelAdapter udpBothAdapter() { + return createUdpAdapter(); + } + + private UdpSyslogReceivingChannelAdapter createUdpAdapter() { + UdpSyslogReceivingChannelAdapter adapter = new UdpSyslogReceivingChannelAdapter(); + setAdapterProperties(adapter); + return adapter; + } + + @Bean + @ConditionalOnProperty(name = "syslog.supplier.protocol", havingValue = "tcp", matchIfMissing = true) + public TcpSyslogReceivingChannelAdapter tcpAdapter( + @Qualifier("syslogSupplierConnectionFactory") AbstractServerConnectionFactory connectionFactory) { + return createTcpAdapter(connectionFactory); + } + + @Bean + @ConditionalOnProperty(name = "syslog.supplier.protocol", havingValue = "both") + public TcpSyslogReceivingChannelAdapter tcpBothAdapter( + @Qualifier("syslogSupplierConnectionFactory") AbstractServerConnectionFactory connectionFactory) { + return createTcpAdapter(connectionFactory); + } + + @Bean + public MessageConverter syslogConverter() { + if (this.properties.getRfc().equals("5424")) { + return new RFC5424MessageConverter(); + } + else { + return new DefaultMessageConverter(); + } + } + + private TcpSyslogReceivingChannelAdapter createTcpAdapter(AbstractServerConnectionFactory connectionFactory) { + TcpSyslogReceivingChannelAdapter adapter = new TcpSyslogReceivingChannelAdapter(); + adapter.setConnectionFactory(connectionFactory); + setAdapterProperties(adapter); + return adapter; + } + + private void setAdapterProperties(SyslogReceivingChannelAdapterSupport adapter) { + adapter.setPort(this.properties.getPort()); + adapter.setConverter(syslogConverter()); + adapter.setOutputChannel(output()); + adapter.setAutoStartup(false); + } + + + @Configuration + @ConditionalOnProperty(name = "syslog.supplier.protocol", havingValue = "tcp", matchIfMissing = true) + protected static class TcpBits { + + @Autowired + private SyslogSupplierProperties properties; + + @Bean + public AbstractServerConnectionFactory syslogSupplierConnectionFactory( + @Qualifier("syslogSupplierDecoder") Deserializer decoder) throws Exception { + AbstractServerConnectionFactory factory; + if (this.properties.isNio()) { + factory = new TcpNioServerConnectionFactory(this.properties.getPort()); + } + else { + factory = new TcpNetServerConnectionFactory(this.properties.getPort()); + } + factory.setLookupHost(this.properties.isReverseLookup()); + factory.setDeserializer(decoder); + factory.setSoTimeout(this.properties.getSocketTimeout()); + return factory; + } + + @Bean + public Deserializer syslogSupplierDecoder() { + ByteArrayLfSerializer decoder = new ByteArrayLfSerializer(); + decoder.setMaxMessageSize(this.properties.getBufferSize()); + if (this.properties.getRfc().equals("5424")) { + return new RFC6587SyslogDeserializer(decoder); + } + else { + return decoder; + } + } + } + + @Configuration + @ConditionalOnProperty(name = "syslog.supplier.protocol", havingValue = "both") + protected static class BothBits extends TcpBits { + + } + +} diff --git a/functions/supplier/syslog-supplier/src/main/java/org/springframework/cloud/fn/supplier/syslog/SyslogSupplierProperties.java b/functions/supplier/syslog-supplier/src/main/java/org/springframework/cloud/fn/supplier/syslog/SyslogSupplierProperties.java new file mode 100644 index 00000000..fa085111 --- /dev/null +++ b/functions/supplier/syslog-supplier/src/main/java/org/springframework/cloud/fn/supplier/syslog/SyslogSupplierProperties.java @@ -0,0 +1,142 @@ +/* + * Copyright 2016-2020 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.fn.supplier.syslog; + +import javax.validation.constraints.AssertTrue; +import javax.validation.constraints.NotNull; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.annotation.Validated; + +@ConfigurationProperties("syslog.supplier") +@Validated +public class SyslogSupplierProperties { + + /** + * the buffer size used when decoding messages; larger messages will be rejected. + */ + private int bufferSize = 2048; + + /** + * Protocol used for SYSLOG (tcp or udp). + */ + private Protocol protocol = Protocol.tcp; + + /** + * The port to listen on. + */ + private int port = 1514; + + /** + * whether or not to use NIO (when supporting a large number of connections). + */ + private boolean nio = false; + + /** + * whether or not to perform a reverse lookup on the incoming socket. + */ + private boolean reverseLookup; + + /** + * the socket timeout. + */ + private int socketTimeout; + + /** + * '5424' or '3164' - the syslog format according to the RFC; 3164 is aka 'BSD' format. + */ + private String rfc = "3164"; + + public int getBufferSize() { + return bufferSize; + } + + public void setBufferSize(int bufferSize) { + this.bufferSize = bufferSize; + } + + public Protocol getProtocol() { + return protocol; + } + + public void setProtocol(Protocol protocol) { + this.protocol = protocol; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public boolean isNio() { + return nio; + } + + public void setNio(boolean nio) { + this.nio = nio; + } + + public boolean isReverseLookup() { + return reverseLookup; + } + + public void setReverseLookup(boolean reverseLookup) { + this.reverseLookup = reverseLookup; + } + + public int getSocketTimeout() { + return socketTimeout; + } + + public void setSocketTimeout(int socketTimeout) { + this.socketTimeout = socketTimeout; + } + + @NotNull + public String getRfc() { + return rfc; + } + + public void setRfc(String rfc) { + this.rfc = rfc; + } + + @AssertTrue(message = "rfc must be 5424 or 3164") + public boolean isSupportedRfc() { + return "5424".equals(this.rfc) || "3164".equals(this.rfc); + } + + public enum Protocol { + /** + * TCP protocol. + */ + tcp, + + /** + * UDP protocol. + */ + udp, + + /** + * Represents both TCP and UDP. + */ + both; + } +} diff --git a/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/AbstractSyslogSupplierTests.java b/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/AbstractSyslogSupplierTests.java new file mode 100644 index 00000000..599f1581 --- /dev/null +++ b/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/AbstractSyslogSupplierTests.java @@ -0,0 +1,107 @@ +/* + * Copyright 2016-2020 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.fn.supplier.syslog; + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.function.Supplier; + +import javax.net.SocketFactory; + +import reactor.core.publisher.Flux; + +import org.springframework.beans.DirectFieldAccessor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.integration.ip.tcp.connection.AbstractServerConnectionFactory; +import org.springframework.integration.ip.udp.UnicastReceivingChannelAdapter; +import org.springframework.integration.syslog.inbound.UdpSyslogReceivingChannelAdapter; +import org.springframework.messaging.Message; +import org.springframework.test.annotation.DirtiesContext; + +import static org.assertj.core.api.Assertions.assertThat; + + +@SpringBootTest(properties = "syslog.supplier.port = 0") +@DirtiesContext +public class AbstractSyslogSupplierTests { + + protected static final String RFC3164_PACKET = "<157>JUL 26 22:08:35 WEBERN TESTING[70729]: TEST SYSLOG MESSAGE"; + + protected static final String RFC5424_PACKET = + "<14>1 2014-06-20T09:14:07+00:00 loggregator d0602076-b14a-4c55-852a-981e7afeed38 DEA - " + + "[exampleSDID@32473 iut=\\\"3\\\" eventSource=\\\"Application\\\" eventID=\\\"1011\\\"]" + + "[exampleSDID@32473 iut=\\\"3\\\" eventSource=\\\"Application\\\" eventID=\\\"1011\\\"] " + + "Removing instance"; + + @Autowired + Supplier>> syslogSupplier; + + @Autowired + protected SyslogSupplierProperties properties; + + @Autowired(required = false) + protected AbstractServerConnectionFactory connectionFactory; + + @Autowired(required = false) + protected UdpSyslogReceivingChannelAdapter udpAdapter; + + protected void sendTcp(String syslog) throws Exception { + int port = getPort(); + Socket socket = SocketFactory.getDefault().createSocket("localhost", port); + socket.getOutputStream().write(syslog.getBytes()); + socket.close(); + } + + private int getPort() throws Exception { + int n = 0; + while (n++ < 100 && !this.connectionFactory.isListening()) { + Thread.sleep(100); + } + assertThat(this.connectionFactory.isListening()).isTrue(); + int port = this.connectionFactory.getPort(); + assertThat(port > 0).isTrue(); + return port; + } + + protected void sendUdp(String syslog) throws Exception { + int port = waitUdp(); + DatagramSocket socket = new DatagramSocket(); + DatagramPacket packet = new DatagramPacket(syslog.getBytes(), syslog.length()); + packet.setSocketAddress(new InetSocketAddress("localhost", port)); + socket.send(packet); + socket.close(); + } + + private int waitUdp() throws Exception { + int n = 0; + DirectFieldAccessor dfa = new DirectFieldAccessor(this.udpAdapter); + while (n++ < 100 && !((UnicastReceivingChannelAdapter) dfa.getPropertyValue("udpAdapter")).isListening()) { + Thread.sleep(100); + } + return ((UnicastReceivingChannelAdapter) dfa.getPropertyValue("udpAdapter")).getPort(); + } + + @SpringBootApplication + public static class SyslogSupplierTestApplication { + + } + +} diff --git a/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/NotNioTests.java b/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/NotNioTests.java new file mode 100644 index 00000000..fd485d96 --- /dev/null +++ b/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/NotNioTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2016-2020 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.fn.supplier.syslog; + +import org.junit.jupiter.api.Test; + +import org.springframework.integration.ip.tcp.connection.TcpNetServerConnectionFactory; +import org.springframework.integration.test.util.TestUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +public class NotNioTests extends AbstractSyslogSupplierTests { + + @Test + public void test() throws Exception { + assertThat(this.connectionFactory).isInstanceOf(TcpNetServerConnectionFactory.class); + assertThat(TestUtils.getPropertyValue(this.connectionFactory, "lookupHost", Boolean.class)).isFalse(); + assertThat(TestUtils.getPropertyValue(this.connectionFactory, "soTimeout")).isEqualTo(0); + assertThat(TestUtils.getPropertyValue(this.connectionFactory, "deserializer.maxMessageSize")).isEqualTo(2048); + } +} diff --git a/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/PropertiesPopulatedTests.java b/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/PropertiesPopulatedTests.java new file mode 100644 index 00000000..5e2c7dbb --- /dev/null +++ b/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/PropertiesPopulatedTests.java @@ -0,0 +1,38 @@ +/* + * Copyright 2016-2020 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.fn.supplier.syslog; + +import org.junit.jupiter.api.Test; + +import org.springframework.integration.ip.tcp.connection.TcpNioServerConnectionFactory; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.test.context.TestPropertySource; + +import static org.assertj.core.api.Assertions.assertThat; + +@TestPropertySource(properties = { "syslog.supplier.port = 0", "syslog.supplier.nio = true", "syslog.supplier.reverseLookup = true", + "syslog.supplier.socketTimeout = 123", "syslog.supplier.bufferSize = 5" }) +public class PropertiesPopulatedTests extends AbstractSyslogSupplierTests { + + @Test + public void test() throws Exception { + assertThat(this.connectionFactory).isInstanceOf(TcpNioServerConnectionFactory.class); + assertThat(TestUtils.getPropertyValue(this.connectionFactory, "lookupHost", Boolean.class)).isTrue(); + assertThat(TestUtils.getPropertyValue(this.connectionFactory, "soTimeout")).isEqualTo(123); + assertThat(TestUtils.getPropertyValue(this.connectionFactory, "deserializer.maxMessageSize")).isEqualTo(5); + } +} diff --git a/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/Tcp3164Tests.java b/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/Tcp3164Tests.java new file mode 100644 index 00000000..648a4130 --- /dev/null +++ b/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/Tcp3164Tests.java @@ -0,0 +1,47 @@ +/* + * Copyright 2016-2020 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.fn.supplier.syslog; + +import java.util.Map; + +import org.junit.jupiter.api.Test; +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + +import org.springframework.messaging.Message; + +import static org.assertj.core.api.Assertions.assertThat; + +public class Tcp3164Tests extends AbstractSyslogSupplierTests { + + @Test + public void test() throws Exception { + final Flux> messageFlux = syslogSupplier.get(); + + final StepVerifier stepVerifier = StepVerifier.create(messageFlux) + .assertNext((message) -> { + assertThat(((Map) message.getPayload()).get("HOST")).isEqualTo("WEBERN"); + } + ) + .thenCancel() + .verifyLater(); + + sendTcp(AbstractSyslogSupplierTests.RFC3164_PACKET + "\n"); + + stepVerifier.verify(); + } +} diff --git a/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/Tcp5424Tests.java b/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/Tcp5424Tests.java new file mode 100644 index 00000000..a0934956 --- /dev/null +++ b/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/Tcp5424Tests.java @@ -0,0 +1,49 @@ +/* + * Copyright 2016-2020 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.fn.supplier.syslog; + +import java.util.Map; + +import org.junit.jupiter.api.Test; +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + +import org.springframework.messaging.Message; +import org.springframework.test.context.TestPropertySource; + +import static org.assertj.core.api.Assertions.assertThat; + +@TestPropertySource(properties = { "syslog.supplier.port = 0", "syslog.supplier.rfc = 5424" }) +public class Tcp5424Tests extends AbstractSyslogSupplierTests { + + @Test + public void test() throws Exception { + final Flux> messageFlux = syslogSupplier.get(); + + final StepVerifier stepVerifier = StepVerifier.create(messageFlux) + .assertNext((message) -> { + assertThat(((Map) message.getPayload()).get("syslog_HOST")).isEqualTo("loggregator"); + } + ) + .thenCancel() + .verifyLater(); + + sendTcp("253 " + RFC5424_PACKET); + + stepVerifier.verify(); + } +} diff --git a/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/TcpAndUdp3164Tests.java b/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/TcpAndUdp3164Tests.java new file mode 100644 index 00000000..da19ce97 --- /dev/null +++ b/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/TcpAndUdp3164Tests.java @@ -0,0 +1,52 @@ +/* + * Copyright 2016-2020 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.fn.supplier.syslog; + +import java.util.Map; + +import org.junit.jupiter.api.Test; +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + +import org.springframework.messaging.Message; +import org.springframework.test.context.TestPropertySource; + +import static org.assertj.core.api.Assertions.assertThat; + +@TestPropertySource(properties = "syslog.supplier.protocol = both") +public class TcpAndUdp3164Tests extends AbstractSyslogSupplierTests { + + @Test + public void test() throws Exception { + final Flux> messageFlux = syslogSupplier.get(); + final StepVerifier stepVerifier = StepVerifier.create(messageFlux) + .assertNext((message) -> { + assertThat(((Map) message.getPayload()).get("HOST")).isEqualTo("WEBERN"); + } + ) + .assertNext((message) -> { + assertThat(((Map) message.getPayload()).get("HOST")).isEqualTo("WEBERN"); + } + ) + .thenCancel() + .verifyLater(); + + sendTcp(RFC3164_PACKET + "\n"); + sendUdp(RFC3164_PACKET); + stepVerifier.verify(); + } +} diff --git a/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/TcpAndUdp5424Tests.java b/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/TcpAndUdp5424Tests.java new file mode 100644 index 00000000..394ce58c --- /dev/null +++ b/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/TcpAndUdp5424Tests.java @@ -0,0 +1,53 @@ +/* + * Copyright 2016-2020 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.fn.supplier.syslog; + +import java.util.Map; + +import org.junit.jupiter.api.Test; +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + +import org.springframework.messaging.Message; +import org.springframework.test.context.TestPropertySource; + +import static org.assertj.core.api.Assertions.assertThat; + +@TestPropertySource(properties = { "syslog.supplier.protocol = both", "syslog.supplier.rfc = 5424" }) +public class TcpAndUdp5424Tests extends AbstractSyslogSupplierTests { + + @Test + public void test() throws Exception { + final Flux> messageFlux = syslogSupplier.get(); + final StepVerifier stepVerifier = StepVerifier.create(messageFlux) + .assertNext((message) -> { + assertThat(((Map) message.getPayload()).get("syslog_HOST")).isEqualTo("loggregator"); + } + ) + .assertNext((message) -> { + assertThat(((Map) message.getPayload()).get("syslog_HOST")).isEqualTo("loggregator"); + } + ) + .thenCancel() + .verifyLater(); + + sendTcp("253 " + RFC5424_PACKET); + sendUdp(RFC5424_PACKET); + stepVerifier.verify(); + } + +} diff --git a/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/Udp3164Tests.java b/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/Udp3164Tests.java new file mode 100644 index 00000000..ab1ccf6a --- /dev/null +++ b/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/Udp3164Tests.java @@ -0,0 +1,52 @@ +/* + * Copyright 2016-2020 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.fn.supplier.syslog; + +import java.util.Map; + +import org.junit.jupiter.api.Test; +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + +import org.springframework.messaging.Message; +import org.springframework.test.context.TestPropertySource; + +import static org.assertj.core.api.Assertions.assertThat; + +@TestPropertySource(properties = "syslog.supplier.protocol = udp") +public class Udp3164Tests extends AbstractSyslogSupplierTests { + + @Test + public void test() throws Exception { + final Flux> messageFlux = syslogSupplier.get(); + + final StepVerifier stepVerifier = StepVerifier.create(messageFlux) + .assertNext((message) -> { + assertThat(((Map) message.getPayload()).get("HOST")).isEqualTo("WEBERN"); + } + ) + .thenCancel() + .verifyLater(); + + sendUdp(RFC3164_PACKET); + + stepVerifier.verify(); + + + } + +} diff --git a/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/Udp5424Tests.java b/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/Udp5424Tests.java new file mode 100644 index 00000000..c64cf307 --- /dev/null +++ b/functions/supplier/syslog-supplier/src/test/java/org/springframework/cloud/fn/supplier/syslog/Udp5424Tests.java @@ -0,0 +1,47 @@ +/* + * Copyright 2016-2020 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.fn.supplier.syslog; + +import java.util.Map; + +import org.junit.jupiter.api.Test; +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + +import org.springframework.messaging.Message; +import org.springframework.test.context.TestPropertySource; + +import static org.assertj.core.api.Assertions.assertThat; + +@TestPropertySource(properties = { "syslog.supplier.protocol = udp", "syslog.supplier.rfc = 5424" }) +public class Udp5424Tests extends AbstractSyslogSupplierTests { + + @Test + public void test() throws Exception { + final Flux> messageFlux = syslogSupplier.get(); + final StepVerifier stepVerifier = StepVerifier.create(messageFlux) + .assertNext((message) -> { + assertThat(((Map) message.getPayload()).get("syslog_HOST")).isEqualTo("loggregator"); + } + ) + .thenCancel() + .verifyLater(); + + sendUdp(RFC5424_PACKET); + stepVerifier.verify(); + } +}