Skip to content

Commit 66482a0

Browse files
chore: improve netty usage (#404)
Signed-off-by: Kavindu Dodanduwa <[email protected]>
1 parent 2c1060c commit 66482a0

File tree

3 files changed

+71
-53
lines changed

3 files changed

+71
-53
lines changed

providers/flagd/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
<!-- we only support unix sockets on linux, via epoll native lib -->
5555
<groupId>io.netty</groupId>
5656
<artifactId>netty-transport-native-epoll</artifactId>
57-
<version>4.1.95.Final</version>
57+
<version>4.1.96.Final</version>
5858
<!-- TODO: with 5+ (still alpha), arm is support and we should package multiple versions -->
5959
<classifier>linux-x86_64</classifier>
6060
</dependency>

providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/grpc/GrpcConnector.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import io.grpc.netty.GrpcSslContexts;
1111
import io.grpc.netty.NettyChannelBuilder;
1212
import io.grpc.stub.StreamObserver;
13+
import io.netty.channel.epoll.Epoll;
1314
import io.netty.channel.epoll.EpollDomainSocketChannel;
1415
import io.netty.channel.epoll.EpollEventLoopGroup;
1516
import io.netty.channel.unix.DomainSocketAddress;
@@ -192,6 +193,11 @@ private static void busyWaitAndCheck(final Long deadline, final AtomicBoolean ch
192193
private static ManagedChannel nettyChannel(final FlagdOptions options) {
193194
// we have a socket path specified, build a channel with a unix socket
194195
if (options.getSocketPath() != null) {
196+
// check epoll availability
197+
if (!Epoll.isAvailable()) {
198+
throw new IllegalStateException("unix socket cannot be used", Epoll.unavailabilityCause());
199+
}
200+
195201
return NettyChannelBuilder
196202
.forAddress(new DomainSocketAddress(options.getSocketPath()))
197203
.eventLoopGroup(new EpollEventLoopGroup())

providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/grpc/GrpcConnectorTest.java

Lines changed: 64 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
import io.netty.channel.epoll.EpollEventLoopGroup;
1111
import io.netty.channel.unix.DomainSocketAddress;
1212
import org.junit.jupiter.api.Test;
13+
import org.junit.jupiter.api.condition.EnabledIf;
14+
import org.junit.jupiter.api.condition.EnabledOnOs;
15+
import org.junit.jupiter.api.condition.OS;
1316
import org.junit.jupiter.params.ParameterizedTest;
1417
import org.junit.jupiter.params.provider.ValueSource;
1518
import org.mockito.MockedConstruction;
@@ -124,8 +127,9 @@ void initialization_fail_with_timeout() throws Exception {
124127
}
125128

126129
@Test
127-
void path_arg_should_build_domain_socket_with_correct_path() {
128-
final String path = "/some/path";
130+
void host_and_port_arg_should_build_tcp_socket() {
131+
final String host = "host.com";
132+
final int port = 1234;
129133

130134
ServiceGrpc.ServiceBlockingStub mockBlockingStub = mock(ServiceGrpc.ServiceBlockingStub.class);
131135
ServiceGrpc.ServiceStub mockStub = mock(ServiceGrpc.ServiceStub.class);
@@ -139,31 +143,25 @@ void path_arg_should_build_domain_socket_with_correct_path() {
139143

140144
try (MockedStatic<NettyChannelBuilder> mockStaticChannelBuilder = mockStatic(NettyChannelBuilder.class)) {
141145

142-
try (MockedConstruction<EpollEventLoopGroup> mockEpollEventLoopGroup = mockConstruction(
143-
EpollEventLoopGroup.class,
144-
(mock, context) -> {
145-
})) {
146-
when(NettyChannelBuilder.forAddress(any(DomainSocketAddress.class))).thenReturn(mockChannelBuilder);
146+
mockStaticChannelBuilder.when(() -> NettyChannelBuilder
147+
.forAddress(anyString(), anyInt())).thenReturn(mockChannelBuilder);
147148

148-
new GrpcConnector(FlagdOptions.builder().socketPath(path).build(), null, null);
149+
final FlagdOptions flagdOptions = FlagdOptions.builder().host(host).port(port).tls(false).build();
150+
new GrpcConnector(flagdOptions, null, null);
149151

150-
// verify path matches
151-
mockStaticChannelBuilder.verify(() -> NettyChannelBuilder
152-
.forAddress(argThat((DomainSocketAddress d) -> {
153-
assertEquals(d.path(), path); // path should match
154-
return true;
155-
})), times(1));
156-
}
152+
// verify host/port matches
153+
mockStaticChannelBuilder.verify(() -> NettyChannelBuilder
154+
.forAddress(host, port), times(1));
157155
}
158156
}
159157
}
160158

161159
@Test
162-
void no_args_socket_env_should_build_domain_socket_with_correct_path() throws Exception {
163-
final String path = "/some/other/path";
164-
165-
new EnvironmentVariables("FLAGD_SOCKET_PATH", path).execute(() -> {
160+
void no_args_host_and_port_env_set_should_build_tcp_socket() throws Exception {
161+
final String host = "server.com";
162+
final int port = 4321;
166163

164+
new EnvironmentVariables("FLAGD_HOST", host, "FLAGD_PORT", String.valueOf(port)).execute(() -> {
167165
ServiceGrpc.ServiceBlockingStub mockBlockingStub = mock(ServiceGrpc.ServiceBlockingStub.class);
168166
ServiceGrpc.ServiceStub mockStub = mock(ServiceGrpc.ServiceStub.class);
169167
NettyChannelBuilder mockChannelBuilder = getMockChannelBuilderSocket();
@@ -177,30 +175,27 @@ void no_args_socket_env_should_build_domain_socket_with_correct_path() throws Ex
177175
try (MockedStatic<NettyChannelBuilder> mockStaticChannelBuilder = mockStatic(
178176
NettyChannelBuilder.class)) {
179177

180-
try (MockedConstruction<EpollEventLoopGroup> mockEpollEventLoopGroup = mockConstruction(
181-
EpollEventLoopGroup.class,
182-
(mock, context) -> {
183-
})) {
184-
mockStaticChannelBuilder.when(() -> NettyChannelBuilder
185-
.forAddress(any(DomainSocketAddress.class))).thenReturn(mockChannelBuilder);
178+
mockStaticChannelBuilder.when(() -> NettyChannelBuilder
179+
.forAddress(anyString(), anyInt())).thenReturn(mockChannelBuilder);
186180

187-
new GrpcConnector(FlagdOptions.builder().build(), null, null);
181+
new GrpcConnector(FlagdOptions.builder().build(), null, null);
188182

189-
//verify path matches & called times(= 1 as we rely on reusable channel)
190-
mockStaticChannelBuilder.verify(() -> NettyChannelBuilder
191-
.forAddress(argThat((DomainSocketAddress d) -> {
192-
return d.path() == path;
193-
})), times(1));
194-
}
183+
// verify host/port matches & called times(= 1 as we rely on reusable channel)
184+
mockStaticChannelBuilder.verify(() -> NettyChannelBuilder.
185+
forAddress(host, port), times(1));
195186
}
196187
}
197188
});
198189
}
199190

191+
192+
/**
193+
* OS Specific test - This test is valid only on Linux system as it rely on epoll availability
194+
* */
200195
@Test
201-
void host_and_port_arg_should_build_tcp_socket() {
202-
final String host = "host.com";
203-
final int port = 1234;
196+
@EnabledOnOs(OS.LINUX)
197+
void path_arg_should_build_domain_socket_with_correct_path() {
198+
final String path = "/some/path";
204199

205200
ServiceGrpc.ServiceBlockingStub mockBlockingStub = mock(ServiceGrpc.ServiceBlockingStub.class);
206201
ServiceGrpc.ServiceStub mockStub = mock(ServiceGrpc.ServiceStub.class);
@@ -214,25 +209,35 @@ void host_and_port_arg_should_build_tcp_socket() {
214209

215210
try (MockedStatic<NettyChannelBuilder> mockStaticChannelBuilder = mockStatic(NettyChannelBuilder.class)) {
216211

217-
mockStaticChannelBuilder.when(() -> NettyChannelBuilder
218-
.forAddress(anyString(), anyInt())).thenReturn(mockChannelBuilder);
212+
try (MockedConstruction<EpollEventLoopGroup> mockEpollEventLoopGroup = mockConstruction(
213+
EpollEventLoopGroup.class,
214+
(mock, context) -> {
215+
})) {
216+
when(NettyChannelBuilder.forAddress(any(DomainSocketAddress.class))).thenReturn(mockChannelBuilder);
219217

220-
final FlagdOptions flagdOptions = FlagdOptions.builder().host(host).port(port).tls(false).build();
221-
new GrpcConnector(flagdOptions, null, null);
218+
new GrpcConnector(FlagdOptions.builder().socketPath(path).build(), null, null);
222219

223-
// verify host/port matches
224-
mockStaticChannelBuilder.verify(() -> NettyChannelBuilder
225-
.forAddress(host, port), times(1));
220+
// verify path matches
221+
mockStaticChannelBuilder.verify(() -> NettyChannelBuilder
222+
.forAddress(argThat((DomainSocketAddress d) -> {
223+
assertEquals(d.path(), path); // path should match
224+
return true;
225+
})), times(1));
226+
}
226227
}
227228
}
228229
}
229230

231+
/**
232+
* OS Specific test - This test is valid only on Linux system as it rely on epoll availability
233+
* */
230234
@Test
231-
void no_args_host_and_port_env_set_should_build_tcp_socket() throws Exception {
232-
final String host = "server.com";
233-
final int port = 4321;
235+
@EnabledOnOs(OS.LINUX)
236+
void no_args_socket_env_should_build_domain_socket_with_correct_path() throws Exception {
237+
final String path = "/some/other/path";
238+
239+
new EnvironmentVariables("FLAGD_SOCKET_PATH", path).execute(() -> {
234240

235-
new EnvironmentVariables("FLAGD_HOST", host, "FLAGD_PORT", String.valueOf(port)).execute(() -> {
236241
ServiceGrpc.ServiceBlockingStub mockBlockingStub = mock(ServiceGrpc.ServiceBlockingStub.class);
237242
ServiceGrpc.ServiceStub mockStub = mock(ServiceGrpc.ServiceStub.class);
238243
NettyChannelBuilder mockChannelBuilder = getMockChannelBuilderSocket();
@@ -246,14 +251,21 @@ void no_args_host_and_port_env_set_should_build_tcp_socket() throws Exception {
246251
try (MockedStatic<NettyChannelBuilder> mockStaticChannelBuilder = mockStatic(
247252
NettyChannelBuilder.class)) {
248253

249-
mockStaticChannelBuilder.when(() -> NettyChannelBuilder
250-
.forAddress(anyString(), anyInt())).thenReturn(mockChannelBuilder);
254+
try (MockedConstruction<EpollEventLoopGroup> mockEpollEventLoopGroup = mockConstruction(
255+
EpollEventLoopGroup.class,
256+
(mock, context) -> {
257+
})) {
258+
mockStaticChannelBuilder.when(() -> NettyChannelBuilder
259+
.forAddress(any(DomainSocketAddress.class))).thenReturn(mockChannelBuilder);
251260

252-
new GrpcConnector(FlagdOptions.builder().build(), null, null);
261+
new GrpcConnector(FlagdOptions.builder().build(), null, null);
253262

254-
// verify host/port matches & called times(= 1 as we rely on reusable channel)
255-
mockStaticChannelBuilder.verify(() -> NettyChannelBuilder.
256-
forAddress(host, port), times(1));
263+
//verify path matches & called times(= 1 as we rely on reusable channel)
264+
mockStaticChannelBuilder.verify(() -> NettyChannelBuilder
265+
.forAddress(argThat((DomainSocketAddress d) -> {
266+
return d.path() == path;
267+
})), times(1));
268+
}
257269
}
258270
}
259271
});

0 commit comments

Comments
 (0)