refactoring and adaptation to clearer containers handling and status endpoints for containers

This commit is contained in:
Arthur Wambst 2025-10-19 22:52:01 +02:00
parent 887f757719
commit 592bfc09b0
No known key found for this signature in database
9 changed files with 349 additions and 152 deletions

View File

@ -13,6 +13,9 @@ import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
/**
* Instances
*/
@ -38,9 +41,12 @@ public class Instance {
@JoinColumn(name = "user_id", nullable = false)
public User owner;
public Instance(String name, int port, User user) {
public String containerId;
public Instance(String name, int port, User user, String containerId) {
this.name = name;
this.port = port;
this.owner = user;
this.containerId = containerId;
}
}

View File

@ -29,6 +29,19 @@ public class InstanceEndpoints {
@Inject InstanceService instanceService;
@Inject UserService userService;
///////////////////////////////////////////////////////////////////////////
///
/// First, we create our Instance and basic functions
@POST
@RolesAllowed("ROOT")
@Path("/{id}/instance")
public Response createInstance(@PathParam("id") Long jiId,
@QueryParam("userId") Long userId) {
jiService.createInstance(jiId, userId);
return Response.ok().build();
}
@GET
@RolesAllowed("ROOT")
@Path("/instances")
@ -36,6 +49,14 @@ public class InstanceEndpoints {
return Response.ok(instanceService.getAllInstances()).build();
}
@GET
@RolesAllowed("ROOT")
@Path("/{id}/all-instances")
public Response getAllInstancesJi(@PathParam("id") Long jiId) {
// TODO : a modif, la c est juste all instances
return Response.ok(instanceService.getAllInstances()).build();
}
@GET
@Authenticated
@Path("/{id}/instances")
@ -45,27 +66,66 @@ public class InstanceEndpoints {
return Response.ok(jiService.getInstance(jiId, userId)).build();
}
///////////////////////////////////////////////////////////////////////////
/// Now that the Instance is created, create their containers !
@POST
@Path("/{id}/instance")
public Response createInstance(@PathParam("id") Long jiId,
@QueryParam("userId") Long userId) {
jiService.createInstance(jiId, userId);
@RolesAllowed("ROOT")
@Path("/{id}/containers")
public Response createContainers(@PathParam("id") Long jiId) {
jiService.createContainers(jiId);
return Response.ok().build();
}
/// Finally, start/stop the containers
@POST
@RolesAllowed("ROOT")
@Path("/{id}/container/start")
public Response startContainers(@PathParam("id") Long jiId) {
jiService.startContainers(jiId);
return Response.ok().build();
}
@POST
@Path("/{id}/instance/container")
public Response createContainer(@PathParam("id") Long jiId,
@QueryParam("username") String username) {
return Response.ok(jiService.createContainer(jiId, username)).build();
@RolesAllowed("ROOT")
@Path("/{id}/container/stop")
public Response stopContainers(@PathParam("id") Long jiId) {
jiService.stopContainers(jiId);
return Response.ok().build();
}
@GET
@Path("/{id}/instance/container")
public Response
getStatusContainer(@PathParam("id") Long jiId,
@QueryParam("username") String username) {
return Response.ok(jiService.getStatusContainer(jiId, username))
.build();
@Authenticated
@Path("/{id}/containers")
public Response getStatusContainers(@PathParam("id") Long jiId) {
return Response.ok(jiService.getStatusContainers(jiId)).build();
}
@GET
@RolesAllowed("ROOT")
@Path("/{id}/container-admin-lookup")
public Response getStatusContainer(@PathParam("id") Long jiId,
@QueryParam("userId") Long userId) {
return Response.ok(jiService.getStatusContainer(jiId, userId)).build();
}
@GET
@RolesAllowed("ROOT")
@Path("/{id}/container")
public Response getStatusMyContainer(@PathParam("id") Long jiId) {
String name = identity.getPrincipal().getName();
Long userId = userService.getId(name);
return Response.ok(jiService.getStatusContainer(jiId, userId)).build();
}
///////////////////////////////////////////////////////////////////////////
/// Last but not least, be able do delete every container and instance
///
/// TODO
}

View File

@ -1,7 +1,9 @@
package fr.la_banquise.backend.rest;
import fr.la_banquise.backend.data.model.Ji;
import fr.la_banquise.backend.data.model.User;
import fr.la_banquise.backend.services.JiService;
import fr.la_banquise.backend.services.UserService;
import io.quarkus.security.identity.SecurityIdentity;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
@ -21,6 +23,30 @@ public class JiEndpoints {
@Inject SecurityIdentity identity;
@Inject JiService jiService;
@Inject UserService userService;
///////////////////////////////////////////////////////////////////////////
///
/// First, we create our JI and basic functions
@POST
@Path("/create")
@RolesAllowed("ROOT")
public Response createJi(@QueryParam("name") String name,
@QueryParam("date") String date,
@QueryParam("desc") String desc,
@QueryParam("site_id") Long siteId) {
try {
String username = identity.getPrincipal().getName();
User user = userService.getUser(username);
Long id = jiService.createJi(name, desc, date, siteId, user.id).id;
return Response.ok(Map.of("id", id)).build();
} catch (Exception e) {
return Response.status(500)
.entity(Map.of("error", e.getMessage()))
.build();
}
}
@GET
@Path("/listall")
@ -37,22 +63,6 @@ public class JiEndpoints {
}
}
@POST
@Path("/create")
@RolesAllowed("ROOT")
public Response createJi(@QueryParam("name") String name,
@QueryParam("date") String date,
@QueryParam("desc") String desc,
@QueryParam("site_id") Long siteId) {
try {
Long id = jiService.createJi(name, desc, date, siteId).id;
return Response.ok(Map.of("id", id)).build();
} catch (Exception e) {
return Response.status(500)
.entity(Map.of("error", e.getMessage()))
.build();
}
}
@GET
@Path("/{ID}")
@ -67,7 +77,20 @@ public class JiEndpoints {
.build();
}
}
///////////////////////////////////////////////////////////////////////////
/// Now that the JI is created, we want to add users to it and assign them
/// instances, but DONT create their containers yet !
/// Then, when an admin decides it, we create the containers
/// Finally, start/stop the containers
/// => These endpoints are into InstanceEndpoint
///////////////////////////////////////////////////////////////////////////
/// Last but not least, be able do delete every container and instance
@DELETE
@Path("/del")
@RolesAllowed("ROOT")

View File

@ -10,23 +10,20 @@ import com.github.dockerjava.api.model.ExposedPort;
import com.github.dockerjava.api.model.HostConfig;
import com.github.dockerjava.api.model.PortBinding;
import com.github.dockerjava.api.model.Ports;
import fr.la_banquise.backend.rest.response.DockerResponse;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.ArrayList;
import fr.la_banquise.backend.rest.response.DockerResponse;
@ApplicationScoped
public class DockerService {
@Inject
DockerClient dockerClient;
@Inject DockerClient dockerClient;
public String createAndStartNginx() {
ExposedPort tcp80 = ExposedPort.tcp(80);
@ -34,14 +31,16 @@ public class DockerService {
Ports portBindings = new Ports();
portBindings.bind(tcp80, Ports.Binding.bindPort(8081));
HostConfig hostConfig = HostConfig.newHostConfig().withPortBindings(portBindings);
HostConfig hostConfig =
HostConfig.newHostConfig().withPortBindings(portBindings);
Map<String, String> labels = new HashMap<>();
labels.put("test", "banquise");
labels.put("environment", "development");
labels.put("version", "1.0");
labels.put("created-by", "java-docker-api");
CreateContainerResponse container = dockerClient.createContainerCmd("nginx:latest")
CreateContainerResponse container =
dockerClient.createContainerCmd("nginx:latest")
.withName("my-nginx")
.withLabels(labels)
.withExposedPorts(tcp80)
@ -140,9 +139,9 @@ public class DockerService {
* try {
* var inspectResponse =
* dockerClient.inspectContainerCmd(container.getId()).exec(); if
* (inspectResponse.getConfig().getExposedPorts() != null) { var exposedPorts =
* inspectResponse.getConfig().getExposedPorts(); int count = 0; for (var port
* : exposedPorts) { if (count > 0) portsList.append(",");
* (inspectResponse.getConfig().getExposedPorts() != null) { var
* exposedPorts = inspectResponse.getConfig().getExposedPorts(); int count =
* 0; for (var port : exposedPorts) { if (count > 0) portsList.append(",");
* portsList.append(String.format("\"%s\"",
* port.toString())); count = 1;
* }
@ -168,13 +167,18 @@ public class DockerService {
*/
////////////////////
///
///////////////////////////////////////////////////////////////////////////
///
/// First, we create our container and basic functions
public String createContainer(String name, int port) {
ExposedPort tcpSsh = ExposedPort.tcp(2222);
Ports portBindings = new Ports();
portBindings.bind(tcpSsh, Ports.Binding.bindPort(port));
HostConfig hostConfig = HostConfig.newHostConfig().withPortBindings(portBindings);
HostConfig hostConfig =
HostConfig.newHostConfig().withPortBindings(portBindings);
Map<String, String> labels = new HashMap<>();
labels.put("test", "banquise");
labels.put("environment", "development");
@ -188,36 +192,21 @@ public class DockerService {
.exec(new BuildImageResultCallback())
.awaitImageId();
CreateContainerResponse container = dockerClient.createContainerCmd("ji-python:latest")
CreateContainerResponse container =
dockerClient.createContainerCmd("ji-python:latest")
.withName(name)
.withLabels(labels)
.withExposedPorts(tcpSsh)
.withHostConfig(hostConfig)
.withEnv(
"SUDO_ACCESS=false",
"PASSWORD_ACCESS=true",
"USER_NAME=test", // TODO: CHANGE THIS
"USER_PASSWORD=test" // AND this also oc
.withEnv("SUDO_ACCESS=false", "PASSWORD_ACCESS=true",
"USER_NAME=test", // TODO : User login
"USER_PASSWORD=test" // TODO : Random passwd
)
.exec();
return container.getId();
}
public InspectContainerResponse.ContainerState getStatusContainer(String name) {
InspectContainerResponse container = dockerClient.inspectContainerCmd(name).exec();
return container.getState();
}
public boolean remove(String name) {
try {
dockerClient.removeContainerCmd(name).exec();
return true;
} catch (Exception e) {
return false;
}
}
public boolean containerExists(String name) {
try {
dockerClient.inspectContainerCmd(name).exec();
@ -227,6 +216,31 @@ public class DockerService {
}
}
public List<DockerResponse> listAllContainers() {
List<Container> containers =
dockerClient.listContainersCmd().withShowAll(true).exec();
return containers.stream().map(this::toDockerResponse).toList();
}
private DockerResponse toDockerResponse(Container container) {
List<String> portsList = new ArrayList<>();
for (var port : container.getPorts()) {
portsList.add(String.format("%s:%s", port.getPrivatePort(),
port.getPublicPort()));
}
return new DockerResponse(container.getNames()[0].substring(1),
container.getStatus(), container.getImage(),
portsList);
}
public InspectContainerResponse.ContainerState
getStatusContainer(String name) {
InspectContainerResponse container =
dockerClient.inspectContainerCmd(name).exec();
return container.getState();
}
public void start(String containerId) {
dockerClient.startContainerCmd(containerId).exec();
}
@ -235,21 +249,12 @@ public class DockerService {
dockerClient.stopContainerCmd(containerId).exec();
}
public List<DockerResponse> listAllContainers() {
List<Container> containers = dockerClient.listContainersCmd()
.withShowAll(true)
.exec();
return containers.stream().map(this::toDockerResponse).toList();
public boolean remove(String name) {
try {
dockerClient.removeContainerCmd(name).exec();
return true;
} catch (Exception e) {
return false;
}
private DockerResponse toDockerResponse(Container container) {
List<String> portsList = new ArrayList<>();
for (var port : container.getPorts()) {
portsList.add(String.format("%s:%s", port.getPrivatePort(), port.getPublicPort()));
}
return new DockerResponse(container.getNames()[0].substring(1), container.getStatus(), container.getImage(),
portsList);
}
}

View File

@ -28,6 +28,19 @@ public class InstanceService {
@Inject DockerService dockerService;
///////////////////////////////////////////////////////////////////////////
///
/// First, we create our Instance and basic functions
@Transactional
public Instance createInstance(Long userId, Ji ji) {
User user = userRepository.findById(userId);
String name = user.name + "-" + ji.id;
int port = getFreePort(1).iterator().next();
Instance instance = new Instance(name, port, user, "Not created");
instanceRepository.persist(instance);
return instance;
}
public List<Instance> getAllInstances() {
return instanceRepository.findAll().list();
}
@ -41,41 +54,58 @@ public class InstanceService {
return instanceRepository.findById(id);
}
///////////////////////////////////////////////////////////////////////////
/// Now that the Instance is created, create their containers (and handle
/// if it is already created) !
public void createContainer(Long instanceId) {
Instance instance = instanceRepository.findById(instanceId);
if (instance.containerId.equals("Not created"))
instance.containerId =
dockerService.createContainer(instance.name, instance.port);
}
public InspectContainerResponse.ContainerState getStatusContainer(Long id) {
Instance instance = instanceRepository.findById(id);
return dockerService.getStatusContainer(instance.name);
}
@Transactional
public Instance createInstance(Long id, Ji ji) {
User user = userRepository.findById(id);
String name = user.name + "-" + ji.id;
int port = getFreePort(1).iterator().next();
Instance instance = new Instance(name, port, user);
instanceRepository.persist(instance);
return instance;
}
///////////////////////////////////////////////////////////////////////////
public String createContainer(Long instanceId) {
/// Finally, start/stop the containers
public void startContainer(Long instanceId) {
Instance instance = instanceRepository.findById(instanceId);
return dockerService.createContainer(instance.name, instance.port);
dockerService.start(instance.containerId);
}
public void stopContainer(Long instanceId) {
Instance instance = instanceRepository.findById(instanceId);
dockerService.stop(instance.containerId);
}
///////////////////////////////////////////////////////////////////////////
/// Last but not least, be able do delete every container and instance
public boolean deleteContainer(Long id) {
Instance instance = instanceRepository.findById(id);
return dockerService.remove(instance.name);
}
@Transactional
public boolean deleteInstance(Long id) {
public void deleteInstance(Long id) {
Instance instance = instanceRepository.findById(id);
if (!dockerService.containerExists(instance.name))
return instanceRepository.deleteById(id);
return false;
if (dockerService.containerExists(instance.containerId))
dockerService.remove(instance.containerId);
instanceRepository.deleteById(id);
}
///////////////////////////////////////////////////////////////////////////
///UTILS FOR PORTS NUMBERS HANDLING
public Set<Integer> getUsedPorts() {
Set<Integer> retour = new HashSet<>();
List<Instance> allInstances = getAllInstances();

View File

@ -18,18 +18,22 @@ public class JiService {
@Inject JiRepository jiRepository;
@Inject UserRepository userRepository;
@Inject SiteService siteService;
@Inject UserService userService;
@Inject InstanceService instanceService;
@Inject SecurityContext security;
///////////////////////////////////////////////////////////////////////////
///
/// First, we create our JI and basic functions
@Transactional
public Ji createJi(String name, String description, String date,
Long siteId) {
Long siteId, Long ownerId) {
Site site = siteService.getSite(siteId);
User currentUser =
userRepository.findByName(security.getUserPrincipal().getName());
Ji ji = new Ji(name, description, List.of(currentUser), date, site);
User user = userService.getUser(ownerId);
Ji ji = new Ji(name, description, List.of(user), date, site);
jiRepository.persist(ji);
siteService.addJi(site, ji);
siteService.registerJi(site, ji);
createInstance(user.id, ji.id);
return ji;
}
@ -42,15 +46,13 @@ public class JiService {
public Ji getJi(Long id) { return jiRepository.findById(id); }
@Transactional
public void deleteJi(Long id) {
Ji ji = getJi(id);
siteService.removeJi(ji.site, ji);
jiRepository.deleteById(id);
}
///////////////////////////////////////////////////////////////////////////
/// Now that the JI is created, we want to add users to it and assign them
/// instances, but DONT create their containers yet !
@Transactional
public Instance createInstance(Long jiId, Long userId) {
public Instance createInstance(Long userId, Long jiId) {
Ji ji = jiRepository.findById(jiId);
Instance instance = instanceService.createInstance(userId, ji);
ji.instances.add(instance);
@ -69,34 +71,103 @@ public class JiService {
}
@Transactional
public String createContainer(Long id, String username) {
Ji ji = jiRepository.findById(id);
String retour = "";
public void registerUser(Long userId, Long jiId) {
User user = userService.getUser(userId);
Ji ji = getJi(jiId);
ji.participants.add(user);
createInstance(userId, jiId);
}
///////////////////////////////////////////////////////////////////////////
/// Then, when an admin decides it, we create the containers
/*@Transactional
public void createContainer(Long jiId, Long userId) {
Ji ji = jiRepository.findById(jiId);
String username = userService.getUser(userId).name;
for (Instance instance : ji.instances) {
if (instance.name.equals(username + "-" + id)) {
retour = instanceService.createContainer(instance.id);
if (instance.name.equals(username + "-" + jiId)) {
instanceService.createContainer(instance.id);
break;
}
}
}*/
// On dit a toute les instances de creer leur container.
// Instance gere le cas ou le container existe deja.
@Transactional
public void createContainers(Long jiId) {
Ji ji = jiRepository.findById(jiId);
for (Instance instance : ji.instances) {
instanceService.createContainer(instance.id);
}
}
public String getStatusContainer(Long jiId, Long userId) {
Ji ji = jiRepository.findById(jiId);
String username = userService.getUser(userId).name;
String retour = "";
for (Instance instance : ji.instances) {
if (instance.name.equals(username + "-" + jiId)) {
if (instance.containerId.equals("Not created"))
return "Not created";
retour = instanceService.getStatusContainer(instance.id)
.toString()
.replaceAll(".*status=([^,]+).*", "$1");
break;
}
}
if (retour == "")
throw new Error("instance or container not found");
return "Not created";
return retour;
}
public String getStatusContainer(Long id, String username) {
Ji ji = jiRepository.findById(id);
public String getStatusContainers(Long jiId) {
Ji ji = jiRepository.findById(jiId);
String retour = "";
for (Instance instance : ji.instances) {
if (instance.name.equals(username + "-" + id)) {
retour =
if (instance.containerId.equals("Not created"))
retour += "Not created";
else {
retour +=
instanceService.getStatusContainer(instance.id).toString();
break;
}
}
if (retour == "")
throw new Error("instance or container not found");
return retour;
}
///////////////////////////////////////////////////////////////////////////
/// Finally, start/stop the containers
@Transactional
public void startContainers(Long jiId) {
Ji ji = jiRepository.findById(jiId);
for (Instance instance : ji.instances) {
instanceService.startContainer(instance.id);
}
}
@Transactional
public void stopContainers(Long jiId) {
Ji ji = jiRepository.findById(jiId);
for (Instance instance : ji.instances) {
instanceService.stopContainer(instance.id);
}
}
///////////////////////////////////////////////////////////////////////////
/// Last but not least, be able do delete every container and instance
@Transactional
public void deleteJi(Long jiId) {
Ji ji = getJi(jiId);
for (Instance instance : ji.instances) {
instanceService.deleteInstance(instance.id);
}
siteService.removeJi(ji.site, ji);
jiRepository.deleteById(jiId);
}
}

View File

@ -20,18 +20,12 @@ public class SiteService {
return site;
}
/*@Transactional
public List<Site> saveAllSites(List<Site> sites) {
sites.forEach(site -> siteRepository.persist(site));
return sites;
}*/
public List<Site> getAllSites() { return siteRepository.listAll(); }
public Site getSite(Long id) { return siteRepository.findById(id); }
@Transactional
public void addJi(Site site, Ji ji) {
public void registerJi(Site site, Ji ji) {
site.listJi.add(ji);
}

View File

@ -26,6 +26,7 @@ public class UserService {
@Inject UserRepository userRepository;
@Inject InstanceService instanceService;
@Inject JiService jiService;
@Inject JiRepository jiRepository;
public List<User> getAllUsers() { return userRepository.listAll(); }
@ -35,6 +36,15 @@ public class UserService {
public Long getId(String name) {
return userRepository.findByName(name).id;
}
public boolean userExists(String name) {
try {
if (userRepository.findByName(name) != null)
return true;
return false;
} catch (Exception e) {
return false;
}
}
@Transactional
public UserPostResponse createUser(UserRequest request, String code) {
@ -100,14 +110,12 @@ public class UserService {
if (jiRepository.findById(usersRequest.jiId) != null) {
for (UserRequest user : usersRequest.users) {
try {
Random rand = new Random();
int random = rand.nextInt(8999) + 1000;
createUser(user, String.valueOf(random));
response.successMails.add(user.email);
response.successPasswd.add(user.name +
String.valueOf(random));
} catch (Exception e) {
if (!userExists(user.name)) {
UserPostResponse resp = createUserRandom(user);
response.successMails.add(resp.user.email);
response.successPasswd.add(resp.passwd);
jiService.registerUser(resp.user.id, usersRequest.jiId);
} else {
response.alreadyCreated.add(user.name);
}
}

@ -1 +1 @@
Subproject commit 69955ac4336fc9c492bbe24b23c8098f7afb69ab
Subproject commit afc23d1208d476279930f4852388d30c39258593