Skip to content
Snippets Groups Projects
Commit 5115a2f5 authored by Martin Weise's avatar Martin Weise
Browse files

docker integration test barebone

parent 697937ff
Branches
Tags
2 merge requests!81New stable release,!43Merge dev to master
Showing
with 403 additions and 50 deletions
......@@ -3,10 +3,7 @@ package at.tuwien.endpoints;
import at.tuwien.api.container.*;
import at.tuwien.api.container.network.IpAddressDto;
import at.tuwien.entities.container.Container;
import at.tuwien.exception.ContainerNotFoundException;
import at.tuwien.exception.ContainerStillRunningException;
import at.tuwien.exception.DockerClientException;
import at.tuwien.exception.ImageNotFoundException;
import at.tuwien.exception.*;
import at.tuwien.mapper.ContainerMapper;
import at.tuwien.service.ContainerService;
import io.swagger.annotations.ApiOperation;
......@@ -80,10 +77,14 @@ public class ContainerEndpoint {
public ResponseEntity<ContainerDto> findById(@NotNull @PathVariable Long id) throws DockerClientException, ContainerNotFoundException {
final Container container = containerService.getById(id);
final ContainerDto containerDto = containerMapper.containerToContainerDto(container);
try {
containerService.findIpAddresses(container.getHash())
.forEach((key, value) -> containerDto.setIpAddress(IpAddressDto.builder()
.ipv4(value)
.build()));
} catch (ContainerNotRunningException e) {
throw new DockerClientException("Could not get container IP", e);
}
final ContainerStateDto stateDto = containerService.getContainerState(container.getHash());
try {
containerDto.setState(stateDto);
......
package at.tuwien;
import at.tuwien.api.container.ContainerBriefDto;
import at.tuwien.api.container.ContainerCreateRequestDto;
import at.tuwien.api.container.ContainerDto;
import at.tuwien.api.container.image.ImageBriefDto;
import at.tuwien.api.container.image.ImageCreateDto;
import at.tuwien.api.container.image.ImageDto;
import at.tuwien.api.container.image.ImageEnvItemDto;
import at.tuwien.api.container.network.IpAddressDto;
......@@ -12,6 +14,7 @@ import at.tuwien.entities.container.image.ContainerImageEnvironmentItem;
import com.github.dockerjava.api.command.CreateContainerCmd;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.core.command.CreateContainerCmdImpl;
import com.github.dockerjava.core.exec.CreateContainerCmdExec;
import org.springframework.test.context.TestPropertySource;
import java.math.BigInteger;
......@@ -48,6 +51,8 @@ public abstract class BaseUnitTest {
.value("postgres")
.build())
.toArray(new ImageEnvItemDto[0]);
public final static List<String> IMAGE_1_ENVIRONMENT = List.of("POSTGRES_USER=postgres",
"POSTGRES_PASSWORD=postgres");
public final static String IMAGE_2_REPOSITORY = "redis";
public final static String IMAGE_2_TAG = "latest";
......@@ -74,7 +79,7 @@ public abstract class BaseUnitTest {
.toArray(new ImageEnvItemDto[0]);
public final static ContainerImage IMAGE_1 = ContainerImage.builder()
.id(IMAGE_1_ID)
.id(1L)
.repository(IMAGE_1_REPOSITORY)
.tag(IMAGE_1_TAG)
.hash(IMAGE_1_HASH)
......@@ -117,10 +122,10 @@ public abstract class BaseUnitTest {
public final static Long CONTAINER_1_ID = 1L;
public final static String CONTAINER_1_HASH = "deadbeef";
public final static ContainerImage CONTAINER_1_IMAGE = IMAGE_1;
public final static String CONTAINER_1_NAME = "u01";
public final static String CONTAINER_1_INTERNALNAME = "u01";
public final static String CONTAINER_1_NAME = "fda-userdb-u01";
public final static String CONTAINER_1_INTERNALNAME = "fda-userdb-u01";
public final static String CONTAINER_1_DATABASE = "univie";
public final static String CONTAINER_1_IP = "231.145.98.83";
public final static String CONTAINER_1_IP = "172.28.0.5";
public final static Instant CONTAINER_1_CREATED = Instant.now().minus(1, HOURS);
public final static Long CONTAINER_2_ID = 2L;
......@@ -133,7 +138,7 @@ public abstract class BaseUnitTest {
public final static Instant CONTAINER_2_CREATED = Instant.now().minus(1, HOURS);
public final static Container CONTAINER_1 = Container.builder()
.id(CONTAINER_1_ID)
.id(1L)
.name(CONTAINER_1_NAME)
.internalName(CONTAINER_1_INTERNALNAME)
.image(CONTAINER_1_IMAGE)
......@@ -142,7 +147,7 @@ public abstract class BaseUnitTest {
.build();
public final static Container CONTAINER_2 = Container.builder()
.id(CONTAINER_2_ID)
.id(2L)
.name(CONTAINER_2_NAME)
.internalName(CONTAINER_2_INTERNALNAME)
.image(CONTAINER_2_IMAGE)
......@@ -151,7 +156,6 @@ public abstract class BaseUnitTest {
.build();
public final static ContainerDto CONTAINER_1_DTO = ContainerDto.builder()
.id(CONTAINER_1_ID)
.name(CONTAINER_1_NAME)
.image(IMAGE_1_DTO)
.hash(CONTAINER_1_HASH)
......@@ -162,20 +166,17 @@ public abstract class BaseUnitTest {
.build();
public final static ContainerBriefDto CONTAINER_1_BRIEF_DTO = ContainerBriefDto.builder()
.id(CONTAINER_1_ID)
.name(CONTAINER_1_NAME)
.internalName(CONTAINER_1_INTERNALNAME)
.hash(CONTAINER_1_HASH)
.build();
public final static ImageBriefDto IMAGE_1_BRIEFDTO = ImageBriefDto.builder()
.id(IMAGE_1_ID)
.repository(IMAGE_1_REPOSITORY)
.tag(IMAGE_1_TAG)
.build();
public final static ContainerDto CONTAINER_2_DTO = ContainerDto.builder()
.id(CONTAINER_2_ID)
.name(CONTAINER_2_NAME)
.image(IMAGE_2_DTO)
.hash(CONTAINER_2_HASH)
......@@ -192,5 +193,16 @@ public abstract class BaseUnitTest {
.hash(CONTAINER_2_HASH)
.build();
public final static CreateContainerResponse CONTAINER_1_RESPONSE = new CreateContainerResponse();
public final static ImageCreateDto IMAGE_1_CREATE_DTO = ImageCreateDto.builder()
.repository(IMAGE_1_REPOSITORY)
.tag(IMAGE_1_TAG)
.environment(IMAGE_1_ENV_DTO)
.defaultPort(IMAGE_1_PORT)
.build();
public final static ContainerCreateRequestDto CONTAINER_1_CREATE_DTO = ContainerCreateRequestDto.builder()
.repository(IMAGE_1_REPOSITORY)
.tag(IMAGE_1_TAG)
.name(CONTAINER_1_NAME)
.build();
}
package at.tuwien.service;
import at.tuwien.BaseUnitTest;
import at.tuwien.api.container.ContainerCreateRequestDto;
import at.tuwien.api.container.image.ImageCreateDto;
import at.tuwien.entities.container.image.ContainerImage;
import at.tuwien.exception.ContainerNotRunningException;
import at.tuwien.exception.DockerClientException;
import at.tuwien.repository.ContainerRepository;
import at.tuwien.repository.ImageRepository;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.exception.NotModifiedException;
import com.github.dockerjava.api.model.*;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import javax.transaction.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
@ExtendWith(SpringExtension.class)
@SpringBootTest
public class ContainerServiceIntegrationTest extends BaseUnitTest {
@Autowired
private ContainerService containerService;
@Autowired
private ImageService imageService;
@Autowired
private HostConfig hostConfig;
@Autowired
private ImageRepository imageRepository;
@Autowired
private ContainerRepository containerRepository;
@Autowired
private DockerClient dockerClient;
@Transactional
@BeforeEach
public void beforeEach() {
/* create network */
dockerClient.createNetworkCmd()
.withName("fda-userdb")
.withInternal(true)
.withIpam(new Network.Ipam()
.withConfig(new Network.Ipam.Config()
.withSubnet("172.28.0.0/16")))
.withEnableIpv6(false)
.exec();
imageRepository.save(IMAGE_1);
/* create container */
final CreateContainerResponse request = dockerClient.createContainerCmd(IMAGE_1_REPOSITORY + ":" + IMAGE_1_TAG)
.withEnv(IMAGE_1_ENVIRONMENT)
.withHostConfig(hostConfig.withNetworkMode("fda-userdb")
.withPortBindings(PortBinding.parse("5433:" + IMAGE_1_PORT)))
.withName(CONTAINER_1_NAME)
.withIpv4Address(CONTAINER_1_IP)
.withHostName(CONTAINER_1_INTERNALNAME)
.exec();
/* start container */
dockerClient.startContainerCmd(request.getId()).exec();
CONTAINER_1.setHash(request.getId());
containerRepository.save(CONTAINER_1);
}
@Transactional
@AfterEach
public void afterEach() {
/* stop containers and remove them */
dockerClient.listContainersCmd()
.withShowAll(true)
.exec()
.forEach(container -> {
System.out.println("DELETE CONTAINER " + Arrays.toString(container.getNames()));
try {
dockerClient.stopContainerCmd(container.getId()).exec();
} catch (NotModifiedException e) {
// ignore
}
dockerClient.removeContainerCmd(container.getId()).exec();
});
/* remove networks */
dockerClient.listNetworksCmd()
.exec()
.stream()
.filter(n -> n.getName().startsWith("fda"))
.forEach(network -> {
System.out.println("DELETE NETWORK " + network.getName());
dockerClient.removeNetworkCmd(network.getId()).exec();
});
/* entities are deleted automatically by dirties context */
}
@Test
public void findIpAddress_succeeds() throws ContainerNotRunningException {
/* test */
final Map<String, String> response = containerService.findIpAddresses(CONTAINER_1.getHash());
assertTrue(response.containsKey("fda-userdb"));
assertEquals(CONTAINER_1_IP, response.get("fda-userdb"));
}
@Test
public void findIpAddress_notRunning_fails() {
dockerClient.startContainerCmd(CONTAINER_1.getHash());
/* test */
assertThrows(DockerClientException.class, () -> {
containerService.findIpAddresses(CONTAINER_1.getHash());
});
}
@Test
public void findIpAddress_notFound_fails() {
}
@Test
public void getContainerState_succeeds() {
}
@Test
public void getContainerState_notFound_fails() {
}
@Test
public void create_succeeds() {
final ImageCreateDto request = ImageCreateDto.builder()
.repository(IMAGE_1_REPOSITORY)
.tag(IMAGE_1_TAG)
.defaultPort(IMAGE_1_PORT)
.environment(IMAGE_1_ENV_DTO)
.build();
}
@Test
public void findById_docker_fails() {
// cannot test
}
@Test
public void change_start_succeeds() {
// cannot test
}
@Test
public void change_stop_succeeds() {
// cannot test
}
}
......@@ -30,7 +30,7 @@ import static org.mockito.Mockito.when;
@ExtendWith(SpringExtension.class)
@SpringBootTest
public class ServiceUnitTest extends BaseUnitTest {
public class ContainerServiceUnitTest extends BaseUnitTest {
@Autowired
private ContainerService containerService;
......@@ -124,24 +124,6 @@ public class ServiceUnitTest extends BaseUnitTest {
});
}
@Disabled(value = "cannot test docker api")
@Test
public void findById_docker_fails() {
// cannot test
}
@Disabled(value = "cannot test docker api")
@Test
public void change_start_succeeds() {
// cannot test
}
@Disabled(value = "cannot test docker api")
@Test
public void change_stop_succeeds() {
// cannot test
}
@Test
public void change_start_docker_fails() {
when(containerRepository.findById(CONTAINER_1_ID))
......
package at.tuwien.service;
import at.tuwien.BaseUnitTest;
import at.tuwien.api.container.ContainerCreateRequestDto;
import at.tuwien.api.container.image.ImageChangeDto;
import at.tuwien.api.container.image.ImageCreateDto;
import at.tuwien.entities.container.Container;
import at.tuwien.entities.container.image.ContainerImage;
import at.tuwien.exception.*;
import at.tuwien.repository.ContainerRepository;
import at.tuwien.repository.ImageRepository;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.exception.ConflictException;
import com.github.dockerjava.api.exception.NotFoundException;
import com.github.dockerjava.api.exception.NotModifiedException;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import javax.persistence.EntityNotFoundException;
import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ExtendWith(SpringExtension.class)
@SpringBootTest
public class ImageServiceUnitTest extends BaseUnitTest {
@Autowired
private ImageService imageService;
@MockBean
private ImageRepository imageRepository;
@Test
public void getAll_succeeds() {
when(imageRepository.findAll())
.thenReturn(List.of(IMAGE_1, IMAGE_2));
/* test */
final List<ContainerImage> response = imageService.getAll();
assertEquals(2, response.size());
assertEquals(IMAGE_1_REPOSITORY, response.get(0).getRepository());
assertEquals(IMAGE_1_TAG, response.get(0).getTag());
assertEquals(IMAGE_2_REPOSITORY, response.get(1).getRepository());
assertEquals(IMAGE_2_TAG, response.get(1).getTag());
}
@Test
public void getById_succeeds() {
when(imageRepository.findById(IMAGE_1_ID))
.thenReturn(Optional.of(IMAGE_1));
/* test */
final ContainerImage response = imageService.getById(IMAGE_1_ID);
assertEquals(IMAGE_1_REPOSITORY, response.getRepository());
assertEquals(IMAGE_1_TAG, response.getTag());
}
@Test
public void getById_notFound_fails() {
when(imageRepository.findById(IMAGE_1_ID))
.thenReturn(Optional.empty());
/* test */
assertThrows(ImageNotFoundException.class, () -> {
imageService.getById(IMAGE_1_ID);
});
}
@Test
public void create_notFound_fails() {
final ImageCreateDto request = ImageCreateDto.builder()
.repository(IMAGE_1_REPOSITORY)
.tag(IMAGE_1_TAG)
.defaultPort(IMAGE_1_PORT)
.environment(IMAGE_1_ENV_DTO)
.build();
when(imageService.create(request))
.thenThrow(ImageNotFoundException.class);
/* test */
assertThrows(ImageNotFoundException.class, () -> {
imageService.create(request);
});
}
@Test
public void create_duplicate_fails() {
final ImageCreateDto request = ImageCreateDto.builder()
.repository(IMAGE_1_REPOSITORY)
.tag(IMAGE_1_TAG)
.defaultPort(IMAGE_1_PORT)
.environment(IMAGE_1_ENV_DTO)
.build();
when(imageService.create(request))
.thenThrow(ImageAlreadyExistsException.class);
/* test */
assertThrows(ImageAlreadyExistsException.class, () -> {
imageService.create(request);
});
}
@Test
public void update_succeeds() {
final ImageChangeDto request = ImageChangeDto.builder()
.environment(IMAGE_1_ENV_DTO)
.defaultPort(IMAGE_1_PORT)
.build();
when(imageRepository.findById(IMAGE_1_ID))
.thenReturn(Optional.of(IMAGE_1));
when(imageRepository.save(any()))
.thenReturn(IMAGE_1);
/* test */
final ContainerImage response = imageService.update(IMAGE_1_ID, request);
assertEquals(IMAGE_1_REPOSITORY, response.getRepository());
assertEquals(IMAGE_1_TAG, response.getTag());
}
@Test
public void update_notFound_fails() {
final ImageChangeDto request = ImageChangeDto.builder()
.environment(IMAGE_1_ENV_DTO)
.defaultPort(IMAGE_1_PORT)
.build();
when(imageRepository.findById(IMAGE_1_ID))
.thenReturn(Optional.empty());
/* test */
assertThrows(ImageNotFoundException.class, () -> {
imageService.update(IMAGE_1_ID, request);
});
}
@Test
public void delete_succeeds() {
doNothing()
.when(imageRepository)
.deleteById(IMAGE_1_ID);
/* test */
imageService.delete(IMAGE_1_ID);
}
@Test
public void delete_notFound_fails() {
doThrow(EntityNotFoundException.class)
.when(imageRepository)
.deleteById(IMAGE_1_ID);
/* test */
assertThrows(ImageNotFoundException.class, () -> {
imageService.delete(IMAGE_1_ID);
});
}
}
package at.tuwien.exception;
public class ContainerNotRunningException extends Exception {
public ContainerNotRunningException(String message) {
super(message);
}
public ContainerNotRunningException(String message, Throwable thr) {
super(message, thr);
}
public ContainerNotRunningException(Throwable thr) {
super(thr);
}
}
......@@ -25,6 +25,7 @@ public interface ImageMapper {
@Mappings({
@Mapping(source = "id", target = "hash"),
@Mapping(source = "created", target = "compiled"),
@Mapping(target = "id", ignore = true),
@Mapping(target = "repository", expression = "java(data.getRepoTags().get(0).substring(0,data.getRepoTags().get(0).indexOf(\":\")))"),
@Mapping(target = "tag", expression = "java(data.getRepoTags().get(0).substring(data.getRepoTags().get(0).indexOf(\":\")+1))"),
})
......
......@@ -5,10 +5,7 @@ import at.tuwien.api.container.ContainerDto;
import at.tuwien.api.container.ContainerStateDto;
import at.tuwien.entities.container.Container;
import at.tuwien.entities.container.image.ContainerImage;
import at.tuwien.exception.ContainerNotFoundException;
import at.tuwien.exception.ContainerStillRunningException;
import at.tuwien.exception.DockerClientException;
import at.tuwien.exception.ImageNotFoundException;
import at.tuwien.exception.*;
import at.tuwien.mapper.ContainerMapper;
import at.tuwien.mapper.ImageMapper;
import at.tuwien.repository.ContainerRepository;
......@@ -27,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.SocketUtils;
import javax.transaction.Transactional;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
......@@ -56,6 +54,7 @@ public class ContainerService {
this.imageMapper = imageMapper;
}
@Transactional
public Container create(ContainerCreateRequestDto createDto) throws ImageNotFoundException, DockerClientException {
final Optional<ContainerImage> image = imageRepository.findByRepositoryAndTag(createDto.getRepository(), createDto.getTag());
if (image.isEmpty()) {
......@@ -159,7 +158,7 @@ public class ContainerService {
return container;
}
public Map<String, String> findIpAddresses(String containerHash) throws ContainerNotFoundException {
public Map<String, String> findIpAddresses(String containerHash) throws ContainerNotFoundException, ContainerNotRunningException {
final InspectContainerResponse response;
try {
response = dockerClient.inspectContainerCmd(containerHash)
......@@ -169,6 +168,10 @@ public class ContainerService {
throw new ContainerNotFoundException("container not found", e);
}
final Map<String, String> networks = new HashMap<>();
if (!response.getState().getRunning()) {
log.warn("cannot get IPv4 of container that is not running");
throw new ContainerNotRunningException("container is not running");
}
response.getNetworkSettings()
.getNetworks()
.forEach((key, value) -> {
......
......@@ -91,14 +91,14 @@ public class ImageService {
return imageRepository.save(image);
}
public void delete(Long databaseId) throws ImageNotFoundException {
public void delete(Long id) throws ImageNotFoundException {
try {
imageRepository.deleteById(databaseId);
imageRepository.deleteById(id);
} catch (EntityNotFoundException | EmptyResultDataAccessException e) {
log.error("image id {} not found in metadata database", databaseId);
log.error("image id {} not found in metadata database", id);
throw new ImageNotFoundException("no image with this id found in metadata database.");
}
log.info("deleted image with id {}", databaseId);
log.info("deleted image with id {}", id);
}
/** HELPER FUNCTIONS */
......
......@@ -53,7 +53,7 @@ public class Container {
private Integer port;
@ToString.Include
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.DETACH)
private ContainerImage image;
@Column(nullable = false, updatable = false)
......
......@@ -58,7 +58,7 @@ public class ContainerImage {
private Integer defaultPort;
@ToString.Include
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<ContainerImageEnvironmentItem> environment;
@Column(nullable = false, updatable = false)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment