summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorralph isenmann2021-10-14 14:22:59 +0200
committerralph isenmann2021-10-14 14:22:59 +0200
commitbfd1a084d9ff3a3c2c63824797d6ffe01d947d71 (patch)
tree0e7fb39f66fb5c91d65ba397094cfc8cca7573a3
parent[client] improve workflow of container creation (diff)
downloadtutor-module-bfd1a084d9ff3a3c2c63824797d6ffe01d947d71.tar.gz
tutor-module-bfd1a084d9ff3a3c2c63824797d6ffe01d947d71.tar.xz
tutor-module-bfd1a084d9ff3a3c2c63824797d6ffe01d947d71.zip
[client] update input methods for container images
- remove input method for dockerfiles and git repositories; requires building mechanism in the backend - add input method for docker-archives; tar-files from docker save ...
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/ContainerUploadPageLayout.java123
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/ContainerUploadPage.java98
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/util/ContainerUtils.java83
3 files changed, 201 insertions, 103 deletions
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/ContainerUploadPageLayout.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/ContainerUploadPageLayout.java
index b0201e47..509ba11e 100644
--- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/ContainerUploadPageLayout.java
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/ContainerUploadPageLayout.java
@@ -29,7 +29,8 @@ public class ContainerUploadPageLayout extends WizardPage {
protected final JTabbedPane tpInput;
protected final JTextField txtGitRepo;
- protected final JTextField txtContainerImageFile;
+ protected final JTextField txtImageTar;
+ protected final JButton btnBrowseImageTar;
/**
* Constructor to define the Layout
@@ -39,75 +40,97 @@ public class ContainerUploadPageLayout extends WizardPage {
super(wizard, I18n.PAGE_LAYOUT.getString("ContainerUploadPage.title"));
setDescription(I18n.PAGE_LAYOUT.getString("ContainerUploadPage.description"));
GridManager grid = new GridManager(this, 3, false);
+ GridManager tmpGrid;
- JPanel imageRepoPanel = new JPanel();
- imageRepoPanel.setVisible(true);
- GridManager tmpGrid = new GridManager(imageRepoPanel, 2, true, new Insets(0, 5, 0, 5));
- QLabel lblImageRepo = new QLabel(I18n.PAGE_LAYOUT.getString("ContainerUploadPage.ImageRepo.label"));
- lblImageRepo.setToolTipText(
- I18n.PAGE_LAYOUT.getString("ContainerUploadPage.ImageRepository.ToolTipText"));
- txtImageRepo = new JTextField();
- txtImageRepo.setEditable(true);
- txtImageRepo.setToolTipText(
- I18n.PAGE_LAYOUT.getString("ContainerUploadPage.ImageRepository.ToolTipText"));
- tmpGrid.add(lblImageRepo);
- tmpGrid.add(txtImageRepo).fill(true, false).expand(true, false);
- tmpGrid.finish(false);
-
- JPanel p1 = new JPanel();
- p1.setVisible(false);
- GridManager g1 = new GridManager(p1, 3, true, new Insets(0, 5, 0, 5));
+ // ####################################
+ // Dockerfile Input Panel -- UNUSED --
+ JPanel pnlDockerfileInput = new ContainerTabPanel(ContainerBuildContextMethod.FILE);
+ pnlDockerfileInput.setVisible(false);
+ tmpGrid = new GridManager(pnlDockerfileInput, 3, true, new Insets(0, 5, 0, 5));
QLabel imageFileCaption = new QLabel(
I18n.PAGE_LAYOUT.getString("ContainerUploadPage.DockerFile.label"));
txtImageFile = new JTextField();
txtImageFile.setEditable(false);
btnBrowseForImage = new JButton(I18n.PAGE_LAYOUT.getString("ImageUpload.Button.browseForImage.text"));
btnBrowseForImage.setMnemonic(KeyEvent.VK_B);
- g1.add(imageFileCaption);
- g1.add(txtImageFile).fill(true, false).expand(true, false);
- g1.add(btnBrowseForImage);
- g1.finish(false);
-
- JPanel p2 = new JPanel();
- p2.setVisible(false);
- GridManager g2 = new GridManager(p2, 2, true, new Insets(0, 5, 0, 5));
+ tmpGrid.add(imageFileCaption);
+ tmpGrid.add(txtImageFile).fill(true, false).expand(true, false);
+ tmpGrid.add(btnBrowseForImage);
+ tmpGrid.finish(false);
+
+ // ####################################
+ // Git Repository Input Panel -- UNUSED --
+ JPanel pnlGitRepositoryInput = new ContainerTabPanel(ContainerBuildContextMethod.GIT_REPOSITORY);
+ pnlGitRepositoryInput.setVisible(false);
+ tmpGrid = new GridManager(pnlGitRepositoryInput, 2, true, new Insets(0, 5, 0, 5));
QLabel lblGitRepo = new QLabel(I18n.PAGE_LAYOUT.getString("ContainerUploadPage.GitRepository.label"));
lblGitRepo.setToolTipText(
I18n.PAGE_LAYOUT.getString("ContainerUploadPage.GitRepository.toolTipText"));
txtGitRepo = new JTextField();
txtGitRepo.setToolTipText(
I18n.PAGE_LAYOUT.getString("ContainerUploadPage.GitRepository.toolTipText"));
- g2.add(lblGitRepo);
- g2.add(txtGitRepo).fill(true, false).expand(true, false);
- g2.finish(false);
+ tmpGrid.add(lblGitRepo);
+ tmpGrid.add(txtGitRepo).fill(true, false).expand(true, false);
+ tmpGrid.finish(false);
+
+ // ####################################
+ // Container Archive Input Panel
+ JPanel pnlContainerImage = new ContainerTabPanel(ContainerBuildContextMethod.DOCKER_TAR);
+ pnlGitRepositoryInput.setVisible(false);
+ tmpGrid = new GridManager(pnlContainerImage, 3, true, new Insets(0, 5, 0, 5));
+
+ QLabel lblImageTarFile = new QLabel(
+ I18n.PAGE_LAYOUT.getString("ContainerUploadPage.ContainerImageFile.label"));
+ lblImageTarFile.setToolTipText(
+ I18n.PAGE_LAYOUT.getString("ContainerUploadPage.ContainerImageFile.ToolTipText"));
+
+ txtImageTar = new JTextField();
+ txtImageTar.setEnabled(false);
+ btnBrowseImageTar = new JButton(I18n.PAGE_LAYOUT.getString("ImageUpload.Button.browseForImage.text"));
+ btnBrowseImageTar.setMnemonic(KeyEvent.VK_B);
+ tmpGrid.add(lblImageTarFile);
+ tmpGrid.add(txtImageTar).fill(true, false).expand(true, false);
+ tmpGrid.add(btnBrowseImageTar);
+ tmpGrid.finish(false);
+ // ####################################
+ // Image Repository Input Panel
+ JPanel pnlImageRepo = new ContainerTabPanel(ContainerBuildContextMethod.IMAGE_REPO);
+ pnlImageRepo.setVisible(true);
+ tmpGrid = new GridManager(pnlImageRepo, 2, true, new Insets(0, 5, 0, 5));
+ QLabel lblImageRepo = new QLabel(I18n.PAGE_LAYOUT.getString("ContainerUploadPage.ImageRepo.label"));
+ lblImageRepo.setToolTipText(
+ I18n.PAGE_LAYOUT.getString("ContainerUploadPage.ImageRepository.ToolTipText"));
+ txtImageRepo = new JTextField();
+ txtImageRepo.setEditable(true);
+ txtImageRepo.setToolTipText(
+ I18n.PAGE_LAYOUT.getString("ContainerUploadPage.ImageRepository.ToolTipText"));
+ tmpGrid.add(lblImageRepo);
+ tmpGrid.add(txtImageRepo).fill(true, false).expand(true, false);
+ tmpGrid.finish(false);
+
+ // ####################################
+ // Tabbed Input Pane
tpInput = new JTabbedPane();
- tpInput.addTab("Dockerfile", p1);
- tpInput.addTab("Git-Repository", p2);
- tpInput.addTab("Image-Repository", imageRepoPanel);
+ // TODO we need first a proper backend in bwlp-sat to build container
+ //tpInput.addTab("Dockerfile", pnlDockerfileInput);
+ //tpInput.addTab("Git-Repository", pnlGitRepositoryInput);
+ tpInput.addTab("Docker-Archive", pnlContainerImage);
+ tpInput.addTab("Image-Repository", pnlImageRepo);
tpInput.setSelectedIndex(0);
-
grid.add(tpInput, 3).fill(true, false);
grid.nextRow();
+ // ####################################
+ // Image Name
lblImageName = new QLabel(I18n.PANEL.getString("ContainerPanel.Label.ImageName.text"));
txtImageName = new JTextField();
grid.add(lblImageName);
grid.add(txtImageName, 2, 1).fill(true, false).expand(true, false);
grid.nextRow();
- QLabel lblContainerImageFile = new QLabel(
- I18n.PAGE_LAYOUT.getString("ContainerUploadPage.ContainerImageFile.label"));
- lblContainerImageFile.setToolTipText(
- I18n.PAGE_LAYOUT.getString("ContainerUploadPage.ContainerImageFile.ToolTipText"));
- txtContainerImageFile = new JTextField();
- txtContainerImageFile.setEnabled(false);
- txtContainerImageFile.setToolTipText(
- I18n.PAGE_LAYOUT.getString("ContainerUploadPage.ContainerImageFile.ToolTipText"));
- grid.add(lblContainerImageFile);
- grid.add(txtContainerImageFile, 2).fill(true, false).expand(true, false);
- grid.nextRow();
-
+ // ####################################
+ // Info Box
grid.add(Box.createVerticalGlue(), 3).expand(true, true);
txtInfoText = new JTextArea();
txtInfoText.setBorder(BorderFactory.createTitledBorder(
@@ -126,6 +149,14 @@ public class ContainerUploadPageLayout extends WizardPage {
}
protected ContainerBuildContextMethod getBuildContextMethod() {
- return ContainerBuildContextMethod.fromInt(tpInput.getSelectedIndex());
+ return ((ContainerTabPanel) tpInput.getSelectedComponent()).containerImageContext;
+ }
+
+ static class ContainerTabPanel extends JPanel {
+ public final ContainerBuildContextMethod containerImageContext;
+
+ ContainerTabPanel(ContainerBuildContextMethod imageContext) {
+ this.containerImageContext = imageContext;
+ }
}
}
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/ContainerUploadPage.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/ContainerUploadPage.java
index b21564a6..c7e85d22 100644
--- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/ContainerUploadPage.java
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/ContainerUploadPage.java
@@ -13,9 +13,8 @@ import org.openslx.dozmod.model.ContainerDefinition;
import org.openslx.dozmod.state.UploadWizardState;
import org.openslx.dozmod.thrift.ThriftActions;
import org.openslx.dozmod.thrift.cache.MetaDataCache;
+import org.openslx.dozmod.util.ContainerUtils;
import org.openslx.virtualization.configuration.VirtualizationConfigurationDocker;
-import org.openslx.virtualization.configuration.container.ContainerBuildContextMethod;
-import org.openslx.virtualization.configuration.container.ContainerMeta;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
@@ -23,8 +22,6 @@ import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileFilter;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;
import java.util.regex.Matcher;
@@ -65,8 +62,8 @@ public class ContainerUploadPage extends ContainerUploadPageLayout {
init();
}
- // TODO this constructor is currently used in case if user wants do upload a new version.
- // This makes no currently no sens in context of docker container and this is used.
+ // This constructor is used in case the user wants do upload a new version.
+ // TODO currently unused
public ContainerUploadPage(Wizard wizard, UploadWizardState uploadWizardState,
ImageDetailsRead imageDetailsRead) {
super(wizard);
@@ -129,45 +126,37 @@ public class ContainerUploadPage extends ContainerUploadPageLayout {
}
});
- txtContainerImageFile.addMouseListener(new MouseListener() {
- @Override public void mouseClicked(MouseEvent e) {
- if (e.getClickCount() >= 2)
- browseContainerImageFile();
- }
-
- @Override public void mousePressed(MouseEvent e) {
-
- }
-
- @Override public void mouseReleased(MouseEvent e) {
-
- }
-
- @Override public void mouseEntered(MouseEvent e) {
-
+ this.btnBrowseImageTar.addActionListener(new ActionListener() {
+ @Override public void actionPerformed(ActionEvent e) {
+ browseImageTarFile();
}
+ });
- @Override public void mouseExited(MouseEvent e) {
-
+ this.txtImageTar.addActionListener(new ActionListener() {
+ @Override public void actionPerformed(ActionEvent e) {
+ browseImageTarFile();
}
});
btnBrowseForImage.requestFocus();
}
- private void browseContainerImageFile() {
+ private void browseImageTarFile() {
QFileChooser fc = new QFileChooser(Config.getUploadPath(), false);
fc.setAcceptAllFileFilterUsed(false);
fc.addChoosableFileFilter(new ContainerImageFileFiler());
int action = fc.showOpenDialog(getDialog());
File file = fc.getSelectedFile();
- if (action != JFileChooser.APPROVE_OPTION || file == null) {
- txtContainerImageFile.setText("");
+
+ if (action != JFileChooser.APPROVE_OPTION || file == null || !ContainerUtils.isValidTar(file)) {
+ txtImageTar.setText("");
return;
}
- txtContainerImageFile.setText(file.getAbsolutePath());
+ txtImageTar.setText(file.getAbsolutePath());
+ Config.setUploadPath(file.getParent());
+ reactOnUserInput();
LOGGER.info("Tar File selected");
}
@@ -191,13 +180,6 @@ public class ContainerUploadPage extends ContainerUploadPageLayout {
txtImageName.setText(existingImage.getImageName());
state.descriptionFile = file;
- // TESTING: Upload also a prematurely created image (tar)
- String imageName = file.getParentFile().getName();
- File imageTarFile = new File(file.getParentFile(), imageName.concat(".tar"));
- if (imageTarFile.exists()) {
- txtContainerImageFile.setText(imageTarFile.getAbsolutePath());
- LOGGER.info("Prebuild Container Image found");
- }
Config.setUploadPath(file.getParent());
reactOnUserInput();
@@ -230,9 +212,8 @@ public class ContainerUploadPage extends ContainerUploadPageLayout {
}
private boolean checkUserInput() {
- ContainerBuildContextMethod method = getBuildContextMethod();
- switch (method) {
+ switch (getBuildContextMethod()) {
case IMAGE_REPO:
if (txtImageRepo.getText() == null || txtImageRepo.getText().isEmpty()) {
setWarningMessage(I18n.PAGE.getString("ContainerUploadPage.Warning.NoImageRepo"));
@@ -244,7 +225,7 @@ public class ContainerUploadPage extends ContainerUploadPageLayout {
setWarningMessage(I18n.PAGE.getString("ContainerUploadPage.Warning.NoReceipt"));
return false;
}
- if (! ContainerDefinition.isValidDockerfile(txtImageFile.getText())) {
+ if (!ContainerDefinition.isValidDockerfile(txtImageFile.getText())) {
setWarningMessage(I18n.PAGE.getString("ContainerUploadPage.Warning.NoValidDockerfiler"));
return false;
}
@@ -255,6 +236,16 @@ public class ContainerUploadPage extends ContainerUploadPageLayout {
return false;
}
break;
+ case DOCKER_TAR:
+ if (txtImageTar.getText() == null || txtImageTar.getText().isEmpty()) {
+ setWarningMessage("No Image provided");
+ return false;
+ }
+ break;
+
+ default:
+ // The case is not provided
+ return false;
}
if (txtImageName.getText() == null || txtImageName.getText().isEmpty()) {
@@ -266,27 +257,26 @@ public class ContainerUploadPage extends ContainerUploadPageLayout {
private VirtualizationConfigurationDocker createVirtualizationConfig() {
- ContainerMeta containerMeta = containerDefinition.getContainerMeta();
- containerMeta.setBuildContextMethod(getBuildContextMethod().ordinal());
- containerMeta.setImageName(txtImageName.getText());
-
- File containerImageFile = new File(txtContainerImageFile.getText());
- if (containerImageFile.exists())
- state.diskFile = containerImageFile;
- else
- state.diskFile = getDummyFile();
+ containerDefinition.getContainerMeta().setBuildContextMethod(getBuildContextMethod().ordinal());
+ containerDefinition.getContainerMeta().setImageName(txtImageName.getText());
switch (containerDefinition.getBuildContextMethod()) {
case FILE:
containerDefinition.setContainerRecipe(state.descriptionFile);
+ state.diskFile = getDummyFile();
break;
case IMAGE_REPO:
- containerMeta.setImageRepo(txtImageRepo.getText());
+ containerDefinition.getContainerMeta().setImageRepo(txtImageRepo.getText());
+ state.diskFile = getDummyFile();
state.descriptionFile = getDummyFile();
case GIT_REPOSITORY:
- containerMeta.setBuildContextUrl(txtGitRepo.getText());
+ containerDefinition.getContainerMeta().setBuildContextUrl(txtGitRepo.getText());
+ state.diskFile = getDummyFile();
state.descriptionFile = getDummyFile();
break;
+ case DOCKER_TAR:
+ state.diskFile = new File(txtImageTar.getText());
+ state.descriptionFile = getDummyFile();
}
return containerDefinition.createVirtualizationConfig();
}
@@ -331,10 +321,7 @@ public class ContainerUploadPage extends ContainerUploadPageLayout {
Pattern p = Pattern.compile("[Dd]ockerfile");
Matcher m = p.matcher(f.getName());
- boolean accept = false;
- if ((f.isFile() && m.matches()) || f.isDirectory())
- accept = true;
- return accept;
+ return (f.isFile() && m.matches()) || f.isDirectory();
}
@Override public String getDescription() {
@@ -346,10 +333,7 @@ public class ContainerUploadPage extends ContainerUploadPageLayout {
@Override public boolean accept(File f) {
- boolean accept = false;
- if ((f.isFile() && f.toString().endsWith(".tar")) || f.isDirectory())
- accept = true;
- return accept;
+ return (f.isFile() && f.toString().endsWith(".tar")) || f.isDirectory();
}
@Override public String getDescription() {
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/util/ContainerUtils.java b/dozentenmodul/src/main/java/org/openslx/dozmod/util/ContainerUtils.java
index 0e5a1d15..62e5dd0d 100644
--- a/dozentenmodul/src/main/java/org/openslx/dozmod/util/ContainerUtils.java
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/util/ContainerUtils.java
@@ -1,6 +1,11 @@
package org.openslx.dozmod.util;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonParser;
import org.apache.log4j.Logger;
+import org.kamranzafar.jtar.TarEntry;
+import org.kamranzafar.jtar.TarHeader;
+import org.kamranzafar.jtar.TarInputStream;
import org.openslx.bwlp.thrift.iface.ImageSummaryRead;
import org.openslx.bwlp.thrift.iface.LectureSummary;
import org.openslx.dozmod.gui.Gui;
@@ -10,6 +15,9 @@ import org.openslx.dozmod.thrift.cache.LectureCache;
import org.openslx.thrifthelper.TConst;
import java.awt.*;
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
import java.util.List;
/**
@@ -17,6 +25,7 @@ import java.util.List;
*/
public class ContainerUtils {
+ private static final Logger LOGGER = Logger.getLogger(ContainerUtils.class);
/**
* Checks ImageBaseId of image if already linked with existing Lectures in LectureCache.
*
@@ -40,4 +49,78 @@ public class ContainerUtils {
Gui.showMessageBox(c, I18n.WINDOW.getString("LectureDetails.Message.error.containerLinkedWithLecture"),
MessageType.WARNING, logger, null);
}
+
+ /**
+ * Check if a provided tar file contains information about a single docker image.
+ * To check the validity of the file, the existence of two JSON files is checked
+ * and one of the files must only contain information for one image.
+ *
+ * The tar file have to be created by the 'docker save ...' command.
+ *
+ * @param tarFile the user selected tar file.
+ * @return true if the images imageBaseId is already linked with a lecture.
+ */
+ public static boolean isValidTar(File tarFile) {
+
+ boolean isValid = false;
+ boolean containsManifest = false;
+ boolean containsRepositories = false;
+ JsonArray manifestJson = null;
+
+ try {
+ TarInputStream tis = new TarInputStream(new FileInputStream(tarFile));
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ byte[] rawData = new byte[1024];
+ TarEntry entry;
+
+ // setDefaultSkip seems to fasten things up while processing the file,
+ // because we want only to check manifest.json and repositories file
+ tis.setDefaultSkip(true);
+ while ((entry = tis.getNextEntry()) != null) {
+ if(!TarHeader.USTAR_MAGIC.equals(entry.getHeader().magic.toString()))
+ break;
+ Arrays.fill(rawData, (byte) 0);
+ output.reset();
+ int count = 0;
+
+ if (entry.getName().equals("manifest.json")) {
+ containsManifest = true;
+ while ((count = tis.read(rawData)) != -1) {
+ output.write(rawData, 0, count);
+ }
+ manifestJson = new JsonParser().parse(
+ new String(output.toByteArray(), StandardCharsets.UTF_8)).getAsJsonArray();
+ }
+
+ if (entry.getName().equals("repositories")) {
+ containsRepositories = true;
+ // dont read the file, no checks for the Content
+ }
+
+ if (containsManifest && containsRepositories)
+ break;
+ }
+ tis.close();
+ // check the json files inside the tar file
+ if (containsManifest && containsRepositories && manifestJson.isJsonArray()
+ && manifestJson.size() == 1) {
+ isValid = true;
+ String repoTag = manifestJson.get(0).getAsJsonObject().get("RepoTags").getAsString();
+ LOGGER.info(String.format("Tar File contains Docker Image with repoTag=%s", repoTag));
+ } else if (containsManifest && containsRepositories && manifestJson.isJsonArray()
+ && manifestJson.size() > 1) {
+ Gui.showMessageBox("Tar File container more then one Images!", MessageType.ERROR,
+ LOGGER,
+ null);
+ } else {
+ Gui.showMessageBox("No valid Tar File with Images provided!", MessageType.ERROR,
+ LOGGER,
+ null);
+ }
+
+ } catch (IOException e) {
+ LOGGER.error("IOError while processing tar file", e);
+ }
+ return isValid;
+ }
}