diff --git a/src/main/java/fr/la_banquise/backend/data/model/Instance.java b/src/main/java/fr/la_banquise/backend/data/model/Instance.java index 437a62c..18fbb92 100644 --- a/src/main/java/fr/la_banquise/backend/data/model/Instance.java +++ b/src/main/java/fr/la_banquise/backend/data/model/Instance.java @@ -1,6 +1,9 @@ package fr.la_banquise.backend.data.model; -import com.github.dockerjava.api.model.Container; +import java.util.Set; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; @@ -9,6 +12,7 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; +import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; /** @@ -16,6 +20,7 @@ import lombok.NoArgsConstructor; */ @Entity @NoArgsConstructor +@AllArgsConstructor @Table(name = "instance") public class Instance { @Id @@ -23,23 +28,26 @@ public class Instance { @SequenceGenerator(name = "InstanceSeq", sequenceName = "instance_id_seq", allocationSize = 1, initialValue = 1) public Long id; - public String name; - public Long port; - public Container container; + + @Column(unique = true, nullable = false) + public String name; + // So people cant have more than one + // container per JI because name will be login-jiId + + public int port; //@JsonBackReference + @JsonIgnore @ManyToOne @JoinColumn(name = "user_id", nullable = false) public User owner; - @ManyToOne - @JoinColumn(name = "practical_id", nullable = false) - public Sujet sujet; + //@ManyToOne @JoinColumn(name = "ji_id", nullable = false) public Ji ji; - public Instance(String name, Long port, User user, Sujet sujet) { + public Instance(String name, int port, User user ) {//, Ji ji) { this.name = name; this.port = port; this.owner = user; - this.sujet = sujet; + //this.ji = ji; } } diff --git a/src/main/java/fr/la_banquise/backend/data/model/Ji.java b/src/main/java/fr/la_banquise/backend/data/model/Ji.java index b296feb..852ca12 100644 --- a/src/main/java/fr/la_banquise/backend/data/model/Ji.java +++ b/src/main/java/fr/la_banquise/backend/data/model/Ji.java @@ -9,6 +9,7 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinTable; import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; import java.util.List; @@ -28,16 +29,19 @@ public class Ji { public String name; public String description; + @JsonIgnore @ManyToMany - @JoinTable( - name = "ji_user", // Table de liaison - joinColumns = @JoinColumn(name = "ji_id"), - inverseJoinColumns = @JoinColumn(name = "user_id") - ) + @JoinTable(name = "ji_respos", // Table de liaison + joinColumns = @JoinColumn(name = "ji_id"), + inverseJoinColumns = @JoinColumn(name = "user_id")) public List respos; public String date; - @ManyToOne @JoinColumn(name = "site_id") @JsonIgnore public Site site; + @ManyToOne + @JoinColumn(name = "site_id") //@JsonIgnore + public Site site; + + @OneToMany @JoinColumn(name = "instance_id") public List instances; public Ji(String name, String description, List respos, String date, Site site) { diff --git a/src/main/java/fr/la_banquise/backend/data/model/Site.java b/src/main/java/fr/la_banquise/backend/data/model/Site.java index fcb5a8b..1e07e46 100644 --- a/src/main/java/fr/la_banquise/backend/data/model/Site.java +++ b/src/main/java/fr/la_banquise/backend/data/model/Site.java @@ -1,6 +1,7 @@ package fr.la_banquise.backend.data.model; import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -31,6 +32,9 @@ public class Site { public String description; public String address; + /*@OneToMany(mappedBy = "ji", cascade = CascadeType.ALL) + public List instances;*/ + @OneToMany(mappedBy = "site") @JsonIgnore public List jiInSite; public Site(String name, String description, String address) { @@ -46,5 +50,5 @@ public class Site { description + "', address='" + address + "'}"; } - //public Site() {} + // public Site() {} } diff --git a/src/main/java/fr/la_banquise/backend/data/model/User.java b/src/main/java/fr/la_banquise/backend/data/model/User.java index 03af65a..3bd8f5e 100644 --- a/src/main/java/fr/la_banquise/backend/data/model/User.java +++ b/src/main/java/fr/la_banquise/backend/data/model/User.java @@ -19,6 +19,7 @@ import jakarta.persistence.ManyToMany; import jakarta.persistence.OneToMany; import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; @@ -73,14 +74,13 @@ public class User { .collect(Collectors.toSet()); } - public User(String name, String password, RolesAsso role, - List instances) { + public User(String name, String password, RolesAsso role) { this.name = name; this.password = password; if (role == RolesAsso.NONE) this.role = new HashSet<>(); else this.role = new HashSet<>(Arrays.asList(role)); - this.instances = instances; + this.instances = new ArrayList<>(); } } diff --git a/src/main/java/fr/la_banquise/backend/rest/Endpoints.java b/src/main/java/fr/la_banquise/backend/rest/Endpoints.java index c6f9a75..21c90aa 100644 --- a/src/main/java/fr/la_banquise/backend/rest/Endpoints.java +++ b/src/main/java/fr/la_banquise/backend/rest/Endpoints.java @@ -28,7 +28,7 @@ public class Endpoints { DashboardResponse dashboard = new DashboardResponse(); dashboard.tps = sujetService.getAllSujetsRespo(identity.getPrincipal().getName()); - dashboard.instances = instanceService.getAllInstances(username); + //dashboard.instances = instanceService.getAllInstances(username); return Response.ok(dashboard).build(); } } diff --git a/src/main/java/fr/la_banquise/backend/rest/InstanceEndpoints.java b/src/main/java/fr/la_banquise/backend/rest/InstanceEndpoints.java index 39b7d7b..23c7339 100644 --- a/src/main/java/fr/la_banquise/backend/rest/InstanceEndpoints.java +++ b/src/main/java/fr/la_banquise/backend/rest/InstanceEndpoints.java @@ -1,38 +1,57 @@ package fr.la_banquise.backend.rest; -import fr.la_banquise.backend.rest.request.InstanceRequest; import fr.la_banquise.backend.services.InstanceService; +import fr.la_banquise.backend.services.JiService; import io.quarkus.security.identity.SecurityIdentity; +import jakarta.annotation.security.RolesAllowed; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; /** * InstanceEndpoints */ -@Path("/api/instances") +@Path("/api/ji/") @Produces(MediaType.APPLICATION_JSON) public class InstanceEndpoints { - @Inject - SecurityIdentity identity; + @Inject SecurityIdentity identity; - @Inject - InstanceService instanceService; + @Inject JiService jiService; + @Inject InstanceService instanceService; @GET + @RolesAllowed("ROOT") + @Path("/instances") public Response getAllInstances() { - String username = identity.getPrincipal().getName(); - return Response.ok(instanceService.getAllInstances(username)).build(); + return Response.ok(instanceService.getAllInstances()).build(); } @POST - public Response createInstance(InstanceRequest request) { - instanceService.createInstance(request.name, request.ssh, request.pwd, request.port, request.username, request.tpId); + @Path("/{id}/instance") + public Response createInstance(@PathParam("id") Long jiId, + @QueryParam("username") String username) { + jiService.createInstance(jiId, username); 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(); + } + + @GET + @Path("/{id}/instance/container") + public Response getStatusContainer(@PathParam("id") Long jiId, + @QueryParam("username") String username) { + return Response.ok(jiService.getStatusContainer(jiId, username)).build(); + } } diff --git a/src/main/java/fr/la_banquise/backend/rest/JiResource.java b/src/main/java/fr/la_banquise/backend/rest/JiResource.java index 3fbb282..3a9bc31 100644 --- a/src/main/java/fr/la_banquise/backend/rest/JiResource.java +++ b/src/main/java/fr/la_banquise/backend/rest/JiResource.java @@ -13,7 +13,7 @@ import java.util.Map; import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; import org.eclipse.microprofile.openapi.annotations.responses.APIResponses; -@Path("/ji") +@Path("/api/ji") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class JiResource { @@ -25,7 +25,7 @@ public class JiResource { @GET @Path("/listall") @Produces(MediaType.APPLICATION_JSON) - @RolesAllowed("root") + @RolesAllowed("ROOT") public Response listall() { try { List ji = jiService.getAllJiAdmin(); @@ -39,9 +39,10 @@ public class JiResource { @POST @Path("/create") - @RolesAllowed("root") + @RolesAllowed("ROOT") public Response createJi(@QueryParam("name") String name, - @QueryParam("desc") String desc, + @QueryParam("desc") String desc, // TODO : change + // desc to date @QueryParam("address") String address, @QueryParam("respo") String respo, @QueryParam("site") String name_site) { diff --git a/src/main/java/fr/la_banquise/backend/rest/SiteEndpoints.java b/src/main/java/fr/la_banquise/backend/rest/SiteEndpoints.java index 9e56ce3..2a11467 100644 --- a/src/main/java/fr/la_banquise/backend/rest/SiteEndpoints.java +++ b/src/main/java/fr/la_banquise/backend/rest/SiteEndpoints.java @@ -26,7 +26,7 @@ public class SiteEndpoints { @GET @Produces(MediaType.APPLICATION_JSON) - @RolesAllowed("root") + @RolesAllowed("ROOT") @Operation(summary = "Lists all existing sites", description = "Lists all sites. Root role required.") @APIResponses({ @@ -47,7 +47,7 @@ public class SiteEndpoints { } @POST - @RolesAllowed("root") + @RolesAllowed("ROOT") @Operation(summary = "Creates a site", description = "Creates a site if no name is not a duplicate.") @APIResponses({ diff --git a/src/main/java/fr/la_banquise/backend/rest/SujetEndpoints.java b/src/main/java/fr/la_banquise/backend/rest/SujetEndpoints.java index 726c2e9..8a670dc 100644 --- a/src/main/java/fr/la_banquise/backend/rest/SujetEndpoints.java +++ b/src/main/java/fr/la_banquise/backend/rest/SujetEndpoints.java @@ -43,7 +43,7 @@ public class SujetEndpoints { @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @RolesAllowed("root") + @RolesAllowed("ROOT") public Response createSujet(SujetRequest sujet) { return Response.ok(sujetService.createSujet(sujet)).build(); } @@ -51,7 +51,7 @@ public class SujetEndpoints { @POST @Path("/respo") @Produces(MediaType.APPLICATION_JSON) - @RolesAllowed("root") + @RolesAllowed("ROOT") public Response addRespoSujet(@QueryParam("name_sujet") String name_sujet, @QueryParam("name_user") String name_user) { try { diff --git a/src/main/java/fr/la_banquise/backend/rest/request/InstanceRequest.java b/src/main/java/fr/la_banquise/backend/rest/request/InstanceRequest.java index a157c97..a3e6b62 100644 --- a/src/main/java/fr/la_banquise/backend/rest/request/InstanceRequest.java +++ b/src/main/java/fr/la_banquise/backend/rest/request/InstanceRequest.java @@ -1,16 +1,9 @@ package fr.la_banquise.backend.rest.request; -import io.smallrye.common.constraint.Nullable; - /** * InstanceRequest */ public class InstanceRequest { - public String name; - public String ssh; - public String pwd; public String username; - public Long port; - @Nullable - public Long tpId; + public Long jiId; } diff --git a/src/main/java/fr/la_banquise/backend/services/InstanceService.java b/src/main/java/fr/la_banquise/backend/services/InstanceService.java index 7b8cf37..b02d6a2 100644 --- a/src/main/java/fr/la_banquise/backend/services/InstanceService.java +++ b/src/main/java/fr/la_banquise/backend/services/InstanceService.java @@ -1,15 +1,27 @@ package fr.la_banquise.backend.services; +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.model.ExposedPort; +import com.github.dockerjava.api.model.HostConfig; +import com.github.dockerjava.api.model.Ports; + import fr.la_banquise.backend.data.model.Instance; -import fr.la_banquise.backend.data.model.Sujet; +import fr.la_banquise.backend.data.model.Ji; import fr.la_banquise.backend.data.model.User; import fr.la_banquise.backend.data.repository.InstanceRepository; -import fr.la_banquise.backend.data.repository.SujetRepository; +import fr.la_banquise.backend.data.repository.JiRepository; import fr.la_banquise.backend.data.repository.UserRepository; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.transaction.Transactional; + +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; /** * InstanceService @@ -21,7 +33,9 @@ public class InstanceService { @Inject UserRepository userRepository; - @Inject SujetRepository sujetRepository; + @Inject JiRepository jiRepository; + + @Inject DockerClient dockerClient; public List getAllInstances() { return instanceRepository.findAll().list(); @@ -36,44 +50,100 @@ public class InstanceService { return instanceRepository.findById(id); } + public InspectContainerResponse.ContainerState getStatusContainer(Long id) { + Instance instance = instanceRepository.findById(id); + InspectContainerResponse container = + dockerClient.inspectContainerCmd(instance.name).exec(); + return container.getState(); + } + @Transactional - public Instance createInstance(String name, Long port, String username, - Long sujetId) { + public Instance createInstance(String username, Ji ji) { User user = userRepository.findByName(username); - Sujet sujet = sujetRepository.findById(sujetId); - Instance instance = new Instance(name, port, user, sujet); + String name = username + "-" + ji.id; + int port = getFreePort(1).iterator().next(); + Instance instance = new Instance(name, port, user); instanceRepository.persist(instance); return instance; } - @Transactional - public Instance createInstance(String name, String ssh, String pwd, - Long port, User user, Long sujetId) { - Sujet sujet = sujetRepository.findById(sujetId); - Instance instance = new Instance(name, port, user, sujet); - instanceRepository.persist(instance); - return instance; + public String createContainer(Long instanceId) { + Instance instance = instanceRepository.findById(instanceId); + + ExposedPort tcpSsh = ExposedPort.tcp(22); + + Ports portBindings = new Ports(); + portBindings.bind(tcpSsh, Ports.Binding.bindPort(instance.port)); + + HostConfig hostConfig = + HostConfig.newHostConfig().withPortBindings(portBindings); + Map 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") + .withName(instance.name) + .withLabels(labels) + .withExposedPorts(tcpSsh) + .withHostConfig(hostConfig) + .exec(); + + return container.getId(); + } + + public boolean deleteContainer(String containerName) { + try { + dockerClient.removeContainerCmd(containerName).exec(); + return true; + } catch (Exception e) { + return false; + } } @Transactional public boolean deleteInstance(Long id) { - return instanceRepository.deleteById(id); - } - - @Transactional - public boolean deleteAllInstances() { - instanceRepository.deleteAll(); - return true; - } - - @Transactional - public void deleteJDMIInstances() { - instanceRepository.deleteAll(); - } - - @Transactional - public Instance updateInstance(Long id) { Instance instance = instanceRepository.findById(id); - return instance; + if (!containerExists(instance.name)) + return instanceRepository.deleteById(id); + return false; + } + + public boolean containerExists(String containerName) { + try { + dockerClient.inspectContainerCmd(containerName).exec(); + return true; + } catch (Exception e) { + return false; + } + } + + public Set getUsedPorts() { + Set retour = new HashSet<>(); + List allInstances = getAllInstances(); + for (Instance instance : allInstances) { + retour.add(instance.port); + } + + return retour; + } + + public Set getFreePort(int nbPortsNeeded) { + Set used = getUsedPorts(); + Set retour = new HashSet<>(); + + int port = 40000; + + // max 1000 ports used at a same time + while (retour.size() < nbPortsNeeded && port < 41000) { + if (!used.contains(port)) { + retour.add(port); + } + port++; + } + + return retour; } } diff --git a/src/main/java/fr/la_banquise/backend/services/JiService.java b/src/main/java/fr/la_banquise/backend/services/JiService.java index cb690e7..536e85e 100644 --- a/src/main/java/fr/la_banquise/backend/services/JiService.java +++ b/src/main/java/fr/la_banquise/backend/services/JiService.java @@ -1,5 +1,6 @@ package fr.la_banquise.backend.services; +import fr.la_banquise.backend.data.model.Instance; import fr.la_banquise.backend.data.model.Ji; import fr.la_banquise.backend.data.model.Site; import fr.la_banquise.backend.data.model.User; @@ -9,7 +10,6 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.transaction.Transactional; import jakarta.ws.rs.core.SecurityContext; - import java.util.List; @ApplicationScoped @@ -18,6 +18,7 @@ public class JiService { @Inject JiRepository jiRepository; @Inject UserRepository userRepository; @Inject SiteService siteService; + @Inject InstanceService instanceService; @Inject SecurityContext security; @Transactional @@ -58,4 +59,44 @@ public class JiService { siteService.removeJi(ji.site, ji); jiRepository.delete(ji); } + + @Transactional + public Instance createInstance(Long id, String username) { + Ji ji = jiRepository.findById(id); + Instance instance = instanceService.createInstance(username, ji); + ji.instances.add(instance); + return instance; + } + + @Transactional + public String createContainer(Long id, String username) { + Ji ji = jiRepository.findById(id); + String retour = ""; + for (Instance instance : ji.instances) { + + if (instance.name.equals(username + "-" + id)) { + retour = instanceService.createContainer(instance.id); + break; + } + } + if (retour == "") + throw new Error("instance or container not found"); + + return retour; + } + + public String getStatusContainer(Long id, String username) { + Ji ji = jiRepository.findById(id); + String retour = ""; + for (Instance instance : ji.instances) { + if (instance.name.equals(username + "-" + id)) { + retour = + instanceService.getStatusContainer(instance.id).toString(); + break; + } + } + if (retour == "") + throw new Error("instance or container not found"); + return retour; + } } diff --git a/src/main/java/fr/la_banquise/backend/services/UserService.java b/src/main/java/fr/la_banquise/backend/services/UserService.java index 5da50f1..12300f5 100644 --- a/src/main/java/fr/la_banquise/backend/services/UserService.java +++ b/src/main/java/fr/la_banquise/backend/services/UserService.java @@ -35,7 +35,7 @@ public class UserService { public User createUser(UserRequest request) { User user = new User(request.name, BcryptUtil.bcryptHash(request.password), - RolesAsso.NONE, new ArrayList<>()); + RolesAsso.NONE); userRepository.persist(user); return user; } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index f908816..46f7133 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -37,7 +37,7 @@ quarkus.hibernate-orm.database.generation=drop-and-create quarkus.quinoa.dev-server.port=5173 quarkus.quinoa.enable-spa-routing=true -quarkus.docker.docker-host=unix:///run/user/1000/docker.sock +quarkus.docker.docker-host=unix:///run/user/1001/docker.sock #quarkus.security.auth.enabled-in-dev-mode=false quarkus.hibernate-orm.sql-load-script=import-dev.sql diff --git a/src/main/resources/import-dev.sql b/src/main/resources/import-dev.sql index ace0c48..dc81df7 100644 --- a/src/main/resources/import-dev.sql +++ b/src/main/resources/import-dev.sql @@ -1,3 +1,6 @@ -- Ce fichier est exécuté automatiquement en mode dev INSERT INTO penguin (name, password) VALUES ('root', '$2a$10$lzKAv4aj6s0jtneg0Ikx/eEBb6p.6N6yo7ZF.myqYxEA9MWbMwvNu'); INSERT INTO user_roles (User_id, role) VALUES (1, 'ROOT'); +INSERT INTO site (name, description, address) VALUES ('test', 'test', 'test'); +INSERT INTO ji (name, description, date, site_id) VALUES ('ji', 'test', 'date', 1); +INSERT INTO ji_respos (ji_id, User_id) VALUES (1, 1);