Skip to content

Commit 4e99395

Browse files
authored
Fixing container inspect error (mageddo#599)
* fix inspect error * bump docker-java version
1 parent 7e3973e commit 4e99395

File tree

7 files changed

+208
-10
lines changed

7 files changed

+208
-10
lines changed

build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ dependencies {
6868
implementation('dev.failsafe:failsafe:3.3.1')
6969
implementation("io.github.resilience4j:resilience4j-circuitbreaker:2.2.0")
7070

71-
implementation('com.github.docker-java:docker-java-core:3.3.4')
72-
implementation('com.github.docker-java:docker-java-transport-httpclient5:3.3.4')
71+
implementation('com.github.docker-java:docker-java-core:3.4.0')
72+
implementation('com.github.docker-java:docker-java-transport-httpclient5:3.4.0')
7373

7474
implementation('info.picocli:picocli:4.7.6')
7575
implementation('com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.1')

src/main/java/com/mageddo/dnsproxyserver/docker/dataprovider/ContainerFacade.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
import com.github.dockerjava.api.model.Container;
55

66
import java.util.List;
7+
import java.util.Optional;
78

89
public interface ContainerFacade {
910

1011
Container findById(String containerId);
1112

1213
List<Container> findActiveContainers();
1314

14-
InspectContainerResponse inspect(String id);
15+
Optional<InspectContainerResponse> inspect(String id);
1516
}

src/main/java/com/mageddo/dnsproxyserver/docker/dataprovider/ContainerFacadeDefault.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.github.dockerjava.api.DockerClient;
44
import com.github.dockerjava.api.command.InspectContainerResponse;
5+
import com.github.dockerjava.api.exception.NotFoundException;
56
import com.github.dockerjava.api.model.Container;
67
import com.mageddo.dnsproxyserver.docker.application.Containers;
78
import lombok.RequiredArgsConstructor;
@@ -12,6 +13,7 @@
1213
import javax.inject.Singleton;
1314
import java.util.Collections;
1415
import java.util.List;
16+
import java.util.Optional;
1517

1618
@Slf4j
1719
@Default
@@ -44,7 +46,15 @@ public List<Container> findActiveContainers() {
4446
}
4547

4648
@Override
47-
public InspectContainerResponse inspect(String id) {
48-
return this.dockerClient.inspectContainerCmd(id).exec();
49+
public Optional<InspectContainerResponse> inspect(String id) {
50+
try {
51+
return Optional.of(this.dockerClient.inspectContainerCmd(id).exec());
52+
} catch (NotFoundException e) {
53+
log.warn("status=container-not-found, action=inspect-container, containerId={}", id);
54+
} catch (Exception e) {
55+
log.warn("status=unexpected-exception, action=inspect-container, containerId={}, msg={}", id, e.getMessage(), e);
56+
}
57+
58+
return Optional.empty();
4959
}
5060
}

src/main/java/com/mageddo/dnsproxyserver/solver/docker/dataprovider/ContainerDAODefault.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public class ContainerDAODefault implements ContainerDAO {
2828
public List<Container> findActiveContainersMatching(HostnameQuery query) {
2929
return this.containerFacade.findActiveContainers()
3030
.stream()
31-
.map(it -> this.containerFacade.inspect(it.getId()))
31+
.flatMap(it -> this.containerFacade.inspect(it.getId()).stream())
3232
.filter(ContainerHostnameMatcher.buildPredicate(query))
3333
.map(ContainerMapper::of)
3434
.toList();

src/main/java/com/mageddo/dnsproxyserver/solver/docker/dataprovider/DpsContainerDAODefault.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public Container findDPSContainer() {
5555
return containers
5656
.stream()
5757
.findFirst()
58-
.map(it -> this.containerFacade.inspect(it.getId()))
58+
.flatMap(it -> this.containerFacade.inspect(it.getId()))
5959
.map(ContainerMapper::of)
6060
.orElse(null);
6161
}
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package com.mageddo.dnsproxyserver.docker;
2+
3+
import com.github.dockerjava.api.DockerClient;
4+
import com.github.dockerjava.api.exception.NotFoundException;
5+
import com.github.dockerjava.api.model.Container;
6+
import com.mageddo.dnsproxyserver.docker.dataprovider.ContainerFacadeDefault;
7+
import testing.templates.docker.ContainerTemplates;
8+
import testing.templates.docker.DockerClientTemplates;
9+
10+
import org.junit.jupiter.api.BeforeEach;
11+
import org.junit.jupiter.api.Test;
12+
import org.junit.jupiter.api.extension.ExtendWith;
13+
import org.mockito.junit.jupiter.MockitoExtension;
14+
import testing.templates.docker.InspectContainerResponseTemplates;
15+
16+
import java.util.ArrayList;
17+
import java.util.Optional;
18+
19+
import static org.junit.jupiter.api.Assertions.assertEquals;
20+
import static org.junit.jupiter.api.Assertions.assertNull;
21+
import static org.mockito.Mockito.doReturn;
22+
import static org.mockito.Mockito.doThrow;
23+
24+
@ExtendWith(MockitoExtension.class)
25+
class ContainerFacadeDefaultTest {
26+
27+
ContainerFacadeDefault dao;
28+
29+
DockerClient dockerClient;
30+
31+
@BeforeEach
32+
void before(){
33+
this.dockerClient = DockerClientTemplates.buildSpy();
34+
this.dao = new ContainerFacadeDefault(this.dockerClient);
35+
}
36+
37+
@Test
38+
void mustFindContainerById(){
39+
// arrange
40+
final var mockReturn = new ArrayList<Container>();
41+
mockReturn.add(ContainerTemplates.buildDpsContainer());
42+
mockReturn.add(ContainerTemplates.buildRegularContainerCoffeeMakerCheckout());
43+
44+
final var containerId = mockReturn.get(0).getId();
45+
46+
final var inspectContainerCmd = this.dockerClient.listContainersCmd();
47+
doReturn(mockReturn)
48+
.when(inspectContainerCmd)
49+
.exec()
50+
;
51+
52+
// act
53+
final var container = this.dao.findById(containerId);
54+
55+
// assert
56+
assertEquals(mockReturn.get(0), container);
57+
}
58+
59+
@Test
60+
void mustReturnNullWhenFindContainerByIdNotFound(){
61+
// arrange
62+
final var mockReturn = new ArrayList<Container>();
63+
64+
final var containerId = "abc123";
65+
66+
final var listContainerCmd = this.dockerClient.listContainersCmd();
67+
doReturn(mockReturn)
68+
.when(listContainerCmd)
69+
.exec()
70+
;
71+
72+
// act
73+
final var container = this.dao.findById(containerId);
74+
75+
// assert
76+
assertNull(container);
77+
}
78+
79+
@Test
80+
void mustListActiveContainers(){
81+
// arrange
82+
final var expected = new ArrayList<Container>();
83+
expected.add(ContainerTemplates.buildRegularContainerCoffeeMakerCheckout());
84+
85+
final var listContainerCmd = this.dockerClient.listContainersCmd();
86+
doReturn(expected)
87+
.when(listContainerCmd)
88+
.exec()
89+
;
90+
91+
// act
92+
final var findActiveContainersResponse = this.dao.findActiveContainers();
93+
94+
// assert
95+
assertEquals(expected, findActiveContainersResponse);
96+
}
97+
98+
@Test
99+
void mustInspectContainerById(){
100+
// arrange
101+
final var expected = InspectContainerResponseTemplates.ngixWithDefaultBridgeNetworkOnly();
102+
final var containerId = expected.getId();
103+
104+
final var inspectContainerCmd = this.dockerClient.inspectContainerCmd(containerId);
105+
doReturn(expected)
106+
.when(inspectContainerCmd)
107+
.exec()
108+
;
109+
110+
// act
111+
final var inspectResponse = this.dao.inspect(containerId).orElseThrow();
112+
113+
// assert
114+
assertEquals(expected, inspectResponse);
115+
}
116+
117+
@Test
118+
void mustNotThrowErrorWhenInspectContainerNotFound(){
119+
// arrange
120+
final var containerId = "a39bba9a8bab2899";
121+
122+
final var inspectContainerCmd = this.dockerClient.inspectContainerCmd(containerId);
123+
doThrow(new NotFoundException("Container not found"))
124+
.when(inspectContainerCmd)
125+
.exec()
126+
;
127+
128+
// act
129+
final var container = this.dao.inspect(containerId);
130+
131+
// assert
132+
assertEquals(Optional.empty(), container);
133+
}
134+
135+
@Test
136+
void mustNotThrowErrorWhenInspectContainerFails(){
137+
// arrange
138+
final var containerId = "a39bba9a8bab28aa";
139+
140+
final var inspectContainerCmd = this.dockerClient.inspectContainerCmd(containerId);
141+
doThrow(new NullPointerException("Unexpected failure"))
142+
.when(inspectContainerCmd)
143+
.exec()
144+
;
145+
146+
// act
147+
final var container = this.dao.inspect(containerId);
148+
149+
// assert
150+
assertEquals(Optional.empty(), container);
151+
}
152+
}

src/test/java/testing/mocks/DockerClientStub.java

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,60 @@
33
import com.github.dockerjava.api.DockerClientDelegate;
44
import com.github.dockerjava.api.command.ConnectToNetworkCmd;
55
import com.github.dockerjava.api.command.DockerCmdSyncExec;
6+
import com.github.dockerjava.api.command.InspectContainerCmd;
7+
import com.github.dockerjava.api.command.ListContainersCmd;
68
import com.github.dockerjava.core.command.ConnectToNetworkCmdImpl;
9+
import com.github.dockerjava.core.command.InspectContainerCmdImpl;
10+
import com.github.dockerjava.core.command.ListContainersCmdImpl;
711
import lombok.Getter;
812

13+
import javax.annotation.Nonnull;
14+
15+
import java.util.HashMap;
16+
import java.util.Map;
17+
918
import static org.mockito.Mockito.mock;
1019
import static org.mockito.Mockito.spy;
1120

1221
@Getter
1322
public class DockerClientStub extends DockerClientDelegate {
1423

1524
private final ConnectToNetworkCmd connectToNetworkCmd;
16-
private final DockerCmdSyncExec<?, Void> execution;
25+
private final ListContainersCmd listContainersCmd;
26+
private final Map<String, InspectContainerCmd> inspectContainerCmdMap;
27+
28+
private final DockerCmdSyncExec<ConnectToNetworkCmd, Void> connectToNetworkExecution;
29+
private final ListContainersCmd.Exec listContainersExecution;
30+
private final InspectContainerCmd.Exec inspectContainerExecution;
1731

1832
public DockerClientStub() {
19-
this.execution = mock(DockerCmdSyncExec.class);
20-
this.connectToNetworkCmd = spy(new ConnectToNetworkCmdImpl((DockerCmdSyncExec<ConnectToNetworkCmd, Void>) this.execution));
33+
this.connectToNetworkExecution = mock(DockerCmdSyncExec.class);
34+
this.listContainersExecution = mock(ListContainersCmd.Exec.class);
35+
this.inspectContainerExecution = mock(InspectContainerCmd.Exec.class);
36+
37+
this.connectToNetworkCmd = spy(new ConnectToNetworkCmdImpl(this.connectToNetworkExecution));
38+
this.listContainersCmd = spy(new ListContainersCmdImpl(this.listContainersExecution));
39+
this.inspectContainerCmdMap = new HashMap<>();
2140
}
2241

2342
@Override
2443
public ConnectToNetworkCmd connectToNetworkCmd() {
2544
return this.connectToNetworkCmd;
2645
}
46+
47+
@Override
48+
public ListContainersCmd listContainersCmd() {
49+
return this.listContainersCmd;
50+
}
51+
52+
@Override
53+
public InspectContainerCmd inspectContainerCmd(@Nonnull String containerId) {
54+
if (this.inspectContainerCmdMap.containsKey(containerId)) {
55+
return this.inspectContainerCmdMap.get(containerId);
56+
} else {
57+
final var inspectCmd = spy(new InspectContainerCmdImpl(this.inspectContainerExecution, containerId));
58+
inspectContainerCmdMap.put(containerId, inspectCmd);
59+
return inspectCmd;
60+
}
61+
}
2762
}

0 commit comments

Comments
 (0)