Checkpoint: Image deletion interface
Signed-off-by: Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
This commit is contained in:
parent
e37ec4083a
commit
bc6efc5c51
@ -1,17 +1,25 @@
|
|||||||
package net.libertacasa.pubsh.web;
|
package net.libertacasa.pubsh.web;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
|
||||||
import com.github.dockerjava.api.DockerClient;
|
import com.github.dockerjava.api.DockerClient;
|
||||||
import com.github.dockerjava.api.command.BuildImageResultCallback;
|
import com.github.dockerjava.api.command.BuildImageResultCallback;
|
||||||
|
import com.github.dockerjava.api.command.CreateContainerResponse;
|
||||||
import com.github.dockerjava.api.command.InspectExecResponse.Container;
|
import com.github.dockerjava.api.command.InspectExecResponse.Container;
|
||||||
import com.github.dockerjava.api.command.ListContainersCmd;
|
import com.github.dockerjava.api.command.ListContainersCmd;
|
||||||
|
import com.github.dockerjava.api.command.ListImagesCmd;
|
||||||
|
import com.github.dockerjava.api.command.WaitContainerResultCallback;
|
||||||
import com.github.dockerjava.api.model.Image;
|
import com.github.dockerjava.api.model.Image;
|
||||||
import com.github.dockerjava.core.DefaultDockerClientConfig;
|
import com.github.dockerjava.core.DefaultDockerClientConfig;
|
||||||
import com.github.dockerjava.core.DockerClientConfig;
|
import com.github.dockerjava.core.DockerClientConfig;
|
||||||
@ -82,8 +90,11 @@ public class Docker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String buildImage(String targetUser, String osChoice, Integer count) {
|
public static String buildImage(String targetUser, String osChoice, Integer count) {
|
||||||
String dockerfile = "classpath:docker/Dockerfile-" + osChoice;
|
String dockerfile = "/home/georg/tmp/docker/Dockerfile_" + osChoice;
|
||||||
String tag = targetUser + ":sh" + count;
|
String tag = targetUser + ":sh" + count;
|
||||||
|
if (! new File(dockerfile).exists()) {
|
||||||
|
System.out.printf("\nInvalid Dockerfile: %s\n", dockerfile);
|
||||||
|
}
|
||||||
Set<String> tags = new HashSet<String>();
|
Set<String> tags = new HashSet<String>();
|
||||||
tags.add(tag);
|
tags.add(tag);
|
||||||
String imgid = dockerClient.buildImageCmd()
|
String imgid = dockerClient.buildImageCmd()
|
||||||
@ -91,7 +102,39 @@ public class Docker {
|
|||||||
.withPull(false).withNoCache(false).withTags(tags)
|
.withPull(false).withNoCache(false).withTags(tags)
|
||||||
.exec(new BuildImageResultCallback()).awaitImageId();
|
.exec(new BuildImageResultCallback()).awaitImageId();
|
||||||
|
|
||||||
|
System.out.printf("\nBuilt image with ID %s", imgid);
|
||||||
return(imgid);
|
return(imgid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void deleteImage(String username, String id) {
|
||||||
|
List<Image> imagequery = Docker.getImages();
|
||||||
|
|
||||||
|
if (imagequery.toString().contains(id)) {
|
||||||
|
System.out.printf("\nFound image ID %s, deleting ...", id);
|
||||||
|
dockerClient.removeImageCmd(id).withImageId(id).exec();
|
||||||
|
}
|
||||||
|
if (! imagequery.toString().contains(id)) {
|
||||||
|
System.out.printf("\nImage ID %s not found.", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String createContainer(String imgid) {
|
||||||
|
//String containerid = dockerClient.createContainerCmd(imgid).exec();
|
||||||
|
WaitContainerResultCallback resultCallback = new WaitContainerResultCallback();
|
||||||
|
CreateContainerResponse createContainerResponse = dockerClient.createContainerCmd(imgid).exec();
|
||||||
|
dockerClient.waitContainerCmd(createContainerResponse.getId()).exec(resultCallback);
|
||||||
|
try {
|
||||||
|
resultCallback.awaitCompletion();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
String containerid = createContainerResponse.getId();
|
||||||
|
//System.out.println(dockerClient.infoCmd().exec());
|
||||||
|
|
||||||
|
System.out.printf("\nCreated container with ID %s from image ID %s", containerid, imgid);
|
||||||
|
return(containerid);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
|
|||||||
@Override
|
@Override
|
||||||
protected void configure(HttpSecurity http) throws Exception
|
protected void configure(HttpSecurity http) throws Exception
|
||||||
{
|
{
|
||||||
http.csrf().disable();
|
|
||||||
super.configure(http); //.anonymous().disable()
|
super.configure(http); //.anonymous().disable()
|
||||||
http
|
http
|
||||||
.authorizeRequests()
|
.authorizeRequests()
|
||||||
|
@ -21,6 +21,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
|||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
|
import org.springframework.validation.BindingResult;
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
//import org.springframework.web.bind.annotation.DeleteMapping;
|
//import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
@ -36,7 +37,6 @@ import com.github.dockerjava.api.model.Container;
|
|||||||
import com.github.dockerjava.api.model.Image;
|
import com.github.dockerjava.api.model.Image;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
//@SessionAttributes("osChoice")
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@Controller
|
@Controller
|
||||||
public class WebApplication {
|
public class WebApplication {
|
||||||
@ -87,7 +87,7 @@ public class WebApplication {
|
|||||||
//System.out.println(images);
|
//System.out.println(images);
|
||||||
List<Container> containers = Docker.getContainers();
|
List<Container> containers = Docker.getContainers();
|
||||||
//System.out.println(containers);
|
//System.out.println(containers);
|
||||||
|
|
||||||
model.addAttribute("docker_images", images);
|
model.addAttribute("docker_images", images);
|
||||||
model.addAttribute("docker_containers", containers);
|
model.addAttribute("docker_containers", containers);
|
||||||
|
|
||||||
@ -113,30 +113,66 @@ public class WebApplication {
|
|||||||
username = String.valueOf(customClaims.get("username"));
|
username = String.valueOf(customClaims.get("username"));
|
||||||
// [End]
|
// [End]
|
||||||
|
|
||||||
System.out.printf("Deletion triggered for ID %s by %s (%s)", id, userid, username);
|
System.out.printf("\nDeletion triggered for ID %s by %s (%s)", id, userid, username);
|
||||||
|
|
||||||
Docker.deleteContainer(id);
|
Docker.deleteContainer(id);
|
||||||
|
|
||||||
return("redirect:/portal");
|
return("redirect:/portal");
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(path="/frontend/container/add",consumes=MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
@DeleteMapping("/frontend/image/delete/{id}")
|
||||||
public static String addContainer(@RequestBody MultiValueMap<String, String> body, HttpServletRequest request) {
|
public static String deleteImage(@PathVariable String id, HttpServletRequest request, RedirectAttributes redirectAttributes) {
|
||||||
|
// [Start] This block should move to a logging method. It's only job is to print user details to the console.
|
||||||
KeycloakAuthenticationToken principal = (KeycloakAuthenticationToken) request.getUserPrincipal();
|
KeycloakAuthenticationToken principal = (KeycloakAuthenticationToken) request.getUserPrincipal();
|
||||||
String username= null;
|
String username= null;
|
||||||
String userid = principal.getName();
|
String userid = principal.getName();
|
||||||
IDToken token = principal.getAccount().getKeycloakSecurityContext().getIdToken();
|
IDToken token = principal.getAccount().getKeycloakSecurityContext().getIdToken();
|
||||||
Map<String, Object> customClaims = token.getOtherClaims();
|
Map<String, Object> customClaims = token.getOtherClaims();
|
||||||
username = String.valueOf(customClaims.get("username"));
|
username = String.valueOf(customClaims.get("username"));
|
||||||
String osChoice = (String) body.getFirst("osChoice");
|
// [End]
|
||||||
|
|
||||||
System.out.printf("New container with OS %s requested by %s (%s)", osChoice, userid, username);
|
System.out.printf("\nImage deletion triggered for ID %s by %s (%s)", id, userid, username);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Docker.deleteImage(username, id);
|
||||||
|
String returnmessage = "Success - removed image with ID " + id + "!";
|
||||||
|
redirectAttributes.addFlashAttribute("message", returnmessage);
|
||||||
|
} catch (com.github.dockerjava.api.exception.ConflictException exception) {
|
||||||
|
String returnmessage = "Error - Image is still being used by a container.";
|
||||||
|
redirectAttributes.addFlashAttribute("message", returnmessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return("redirect:/portal");
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(path="/frontend/container/add",consumes=MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||||
|
public static String addContainer(@RequestBody MultiValueMap<String, String> body, HttpServletRequest request, RedirectAttributes redirectAttributes) {
|
||||||
|
KeycloakAuthenticationToken principal = (KeycloakAuthenticationToken) request.getUserPrincipal();
|
||||||
|
String username = null;
|
||||||
|
String userid = principal.getName();
|
||||||
|
IDToken token = principal.getAccount().getKeycloakSecurityContext().getIdToken();
|
||||||
|
Map<String, Object> customClaims = token.getOtherClaims();
|
||||||
|
username = String.valueOf(customClaims.get("username"));
|
||||||
|
String os = (String) body.getFirst("osChoice");
|
||||||
|
|
||||||
|
System.out.printf("\nNew container with OS %s requested by %s (%s)\n", os, userid, username);
|
||||||
|
|
||||||
Random rand = new Random();
|
Random rand = new Random();
|
||||||
Integer randomInt = rand.nextInt(9999999-1111);
|
Integer randomInt = rand.nextInt(9999999-1111);
|
||||||
Integer count = randomInt;
|
Integer count = randomInt;
|
||||||
|
|
||||||
//Docker.buildImage(username, osChoice, count);
|
try {
|
||||||
|
String imageid = Docker.buildImage(username, os, count);
|
||||||
|
String returnmessage = "Success - built image with ID " + imageid + " and tag sh" + count + "!";
|
||||||
|
redirectAttributes.addFlashAttribute("message", returnmessage);
|
||||||
|
} catch (Exception exception) {
|
||||||
|
redirectAttributes.addFlashAttribute("message", "Success!");
|
||||||
|
String returnmessage = "Error - failed to build image :-(";
|
||||||
|
redirectAttributes.addFlashAttribute("message", returnmessage);
|
||||||
|
System.out.println(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Docker.createContainer(imageid);
|
||||||
|
|
||||||
return("redirect:/portal");
|
return("redirect:/portal");
|
||||||
}
|
}
|
||||||
|
@ -4,20 +4,32 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="container">
|
<div id="container">
|
||||||
|
<div th:if="${message}" th:text="${message}" th:class="${'alert ' + alertClass}"/></div>
|
||||||
<h1>
|
<h1>
|
||||||
Hello, <span th:text="${username}"></span>.
|
Hello, <span th:text="${username}"></span>.
|
||||||
</h1>
|
</h1>
|
||||||
<div th:if="${attribute01 != null}" th:text="${attribute01}"></div>
|
<div th:if="${attribute01 != null}" th:text="${attribute01}"></div>
|
||||||
<h2>Available Docker Images:</h2>
|
<h2>Available images:</h2>
|
||||||
<table>
|
<table>
|
||||||
<tr th:each="image: ${docker_images}" th:if="${image.repoTags[0] != '<none>:<none>'}">
|
<tr th:each="image: ${docker_images}" th:if="${image.repoTags[0] != '<none>:<none>'}">
|
||||||
<td th:text="${image.repoTags[0]}" />
|
<td>
|
||||||
<td th:text="${image.created}" />
|
<table>
|
||||||
|
<tr th:each="tag: ${image.repoTags}">
|
||||||
|
<td th:text="${tag}"/>
|
||||||
|
<td>
|
||||||
|
<form id="deletion_pseudoform" action="#" th:action="@{'/frontend/image/delete/{id}'(id=${tag})}" th:method="delete" th:tag="${tag}" th:onsubmit="return confirm('Do you really want to delete the imag with tag ' + this.getAttribute('tag') + '?');">
|
||||||
|
<button class="btn btn-danger" id="deletion_submission" type="submit">x</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
<td th:text="${image.created}" />
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>Generate new throw-away shell:</h2>
|
<h2>Generate new throw-away shell:</h2>
|
||||||
<form th:object="${osChoice}" th:id="request_pseudoform" action="#" th:action="@{'/frontend/container/add'}" th:method="post" th:os="${osoption}" th:onsubmit="return confirm('You are about to generate a shell with the OS ' + this.getAttribute('os') + ' - please be patient after you confirm, as the generation may take a short while.');">
|
<form th:object="${osChoice}" th:id="request_pseudoform" action="#" th:action="@{'/frontend/container/add'}" th:method="post" th:os="${osChoice}" th:onsubmit="return confirm('You are about to generate a shell with the OS ' + this.getAttribute('osoption') + ' - please be patient after you confirm, as the generation may take a short while.');">
|
||||||
<select class="form-control" th:object="${osChoice}" name="osChoice">
|
<select class="form-control" th:object="${osChoice}" name="osChoice">
|
||||||
<option value="">Select operating system ...</option>
|
<option value="">Select operating system ...</option>
|
||||||
<option
|
<option
|
||||||
@ -26,14 +38,13 @@
|
|||||||
th:attr="value=${osoption}"
|
th:attr="value=${osoption}"
|
||||||
th:text="${osoption}"></option>
|
th:text="${osoption}"></option>
|
||||||
</select>
|
</select>
|
||||||
<p><span th:text="${osChoice}"></span></p>
|
|
||||||
<button class="btn btn-primary" th:id="request_submission" th:type="submit">Generate</button>
|
<button class="btn btn-primary" th:id="request_submission" th:type="submit">Generate</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h2>Existing Containers:</h2>
|
<h2>Existing containers:</h2>
|
||||||
<table>
|
<table>
|
||||||
<tr th:each="container: ${docker_containers}">
|
<tr th:each="container: ${docker_containers}">
|
||||||
<td th:text="${container.names[0]}" />
|
<td th:text="${container.names[0]}" />
|
||||||
|
Loading…
Reference in New Issue
Block a user