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

docker integration test barebone

parent 1259f33f
No related branches found
No related tags found
4 merge requests!81New stable release,!43Merge dev to master,!23Sprint results,!18Merge Conflicts
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