diff options
| author | ralph isenmann | 2020-09-15 10:18:27 +0200 |
|---|---|---|
| committer | ralph isenmann | 2020-09-15 14:10:57 +0200 |
| commit | f5519288f4cd0666d217482c9e19d5485e072d39 (patch) | |
| tree | 0e716ad729173272846975f65d9eee2bbef1cb26 | |
| parent | [client] refactoring (diff) | |
| download | tutor-module-f5519288f4cd0666d217482c9e19d5485e072d39.tar.gz tutor-module-f5519288f4cd0666d217482c9e19d5485e072d39.tar.xz tutor-module-f5519288f4cd0666d217482c9e19d5485e072d39.zip | |
[client] Show and modify Container Details in DetailsWindow
- If the selected image in the ListImageWindow is a container, then open the DetailsWindow with a container-specific panel.
- dockerfile and container options are displayed
3 files changed, 304 insertions, 25 deletions
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/ImageDetailsWindow.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/ImageDetailsWindow.java index 211ff1b7..83578aa3 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/ImageDetailsWindow.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/ImageDetailsWindow.java @@ -1,23 +1,18 @@ package org.openslx.dozmod.gui.window; -import java.awt.Color; -import java.awt.Frame; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; +import java.awt.*; +import java.awt.event.*; +import java.io.*; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; import javax.swing.AbstractAction; import javax.swing.JComponent; @@ -28,8 +23,15 @@ import javax.swing.KeyStroke; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.stream.JsonReader; import org.apache.log4j.Logger; import org.apache.thrift.TException; +import org.kamranzafar.jtar.TarEntry; +import org.kamranzafar.jtar.TarHeader; +import org.kamranzafar.jtar.TarInputStream; +import org.kamranzafar.jtar.TarOutputStream; import org.openslx.bwlp.thrift.iface.ImageBaseWrite; import org.openslx.bwlp.thrift.iface.ImageDetailsRead; import org.openslx.bwlp.thrift.iface.ImagePermissions; @@ -46,15 +48,13 @@ import org.openslx.dozmod.gui.MainWindow; import org.openslx.dozmod.gui.changemonitor.AbstractControlWrapper; import org.openslx.dozmod.gui.changemonitor.DialogChangeMonitor; import org.openslx.dozmod.gui.changemonitor.DialogChangeMonitor.TextNotEmptyConstraint; -import org.openslx.dozmod.gui.helper.DateTimeHelper; -import org.openslx.dozmod.gui.helper.ExpiryDateChooser; -import org.openslx.dozmod.gui.helper.MessageType; +import org.openslx.dozmod.gui.helper.*; import org.openslx.dozmod.gui.helper.PopupMenu; -import org.openslx.dozmod.gui.helper.UiFeedback; import org.openslx.dozmod.gui.window.UserListWindow.UserAddedCallback; import org.openslx.dozmod.gui.window.layout.ImageDetailsWindowLayout; import org.openslx.dozmod.gui.wizard.ImageUpdateWizard; import org.openslx.dozmod.gui.wizard.LectureWizard; +import org.openslx.dozmod.gui.wizard.page.DockerfileUploadPage; import org.openslx.dozmod.permissions.ImagePerms; import org.openslx.dozmod.thrift.ImageDetailsActions; import org.openslx.dozmod.thrift.Session; @@ -135,6 +135,8 @@ public class ImageDetailsWindow extends ImageDetailsWindowLayout implements UiFe private AbstractControlWrapper<?> changeListenerPermissions; + private ContainerDefinition containerDefinition = new ContainerDefinition(); + /** * Constructor * @@ -434,6 +436,152 @@ public class ImageDetailsWindow extends ImageDetailsWindowLayout implements UiFe }); } + public static class ContainerDefinition { + + /** + * The file to construct a real container, could be an dockerfile or a singularity recipe. + */ + public String containerDescription; + + /** + * Further container information. + */ + public DockerfileUploadPage.ContainerMeta containerMeta; + + public String getDescription() { + return containerDescription; + } + public void setDescription(String description) { + containerDescription = description; + } + + + public void setContainerDescription(byte[] containerDescription) { + this.containerDescription = new String(containerDescription, StandardCharsets.UTF_8); + } + + public void setContainerMeta(byte[] containerMeta) { + Gson gson = new GsonBuilder().create(); + this.containerMeta = gson.fromJson(new JsonReader(new InputStreamReader(new ByteArrayInputStream(containerMeta), + StandardCharsets.UTF_8)), DockerfileUploadPage.ContainerMeta.class); + } + + public DockerfileUploadPage.ContainerMeta getContainerMeta() { + return containerMeta; + } + + + public static ContainerDefinition fromByteArray(byte[] rawTarData) { + + ContainerDefinition containerDef = new ContainerDefinition(); + + try { + TarInputStream tis = new TarInputStream(new GZIPInputStream(new BufferedInputStream(new ByteArrayInputStream(rawTarData)))); + + TarEntry entry; + + while((entry = tis.getNextEntry()) != null) { + int size = (int)entry.getSize(); + byte[] rawData = new byte[size]; + tis.read(rawData,0,size); + + if (entry.getName().equals("dockerfile")) + containerDef.setContainerDescription(rawData); + if (entry.getName().equals("container_meta.json")) + containerDef.setContainerMeta(rawData); + } + + } catch (IOException e) { + e.printStackTrace(); + } + + return containerDef; + } + + /** + * Serializes the ContainerMeta and Container Description (e.g. dockerfile) into an tar.gz archive. + * + * @return A ByteBuffer object of the container definition. Can be uploaded so satellite server. + */ + public ByteBuffer toByteBuffer() { + + ByteBuffer containerDef = null; + + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + TarOutputStream output = new TarOutputStream(new GZIPOutputStream(baos)); + + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + + tarPutFile(output,"container_meta.json", gson.toJson(containerMeta)); + tarPutFile(output,"dockerfile", containerDescription); + + output.close(); + + containerDef = ByteBuffer.wrap(baos.toByteArray()); + + } catch (IOException e) { + LOGGER.warn("Error writing to tar file", e); + } + + return containerDef; + } + } + + private static void tarPutFile(TarOutputStream output, String fileName, String data) throws IOException + { + if (data == null) + return; + tarPutFile(output, fileName, data.getBytes(StandardCharsets.UTF_8)); + } + + private static void tarPutFile(TarOutputStream output, String fileName, byte[] data) throws IOException + { + if (data == null) + return; + output.putNextEntry(new TarEntry( + TarHeader.createHeader(fileName, data.length, Util.unixTime(), false, 0644))); + output.write(data); + } + + /** + * Uploads ByteBuffer data to satellite server and stores it in ImageVersion.VirtConfig + * + * @param imageVersionId Id of the image version, which will be updated. + * @param data New virtConfig to store in database. + */ + private void saveContainerDef(final String imageVersionId, ByteBuffer data) { + + try { + ThriftManager.getSatClient() + .setImageVersionVirtConfig(Session.getSatelliteToken(), imageVersionId, data); + } catch (TException e) { + e.printStackTrace(); + } + } + + /** + * Loads binary file ImageVersion.VirtConfig from the Satellites Database. + * + * @param selected the selected Image Version. TODO: For now only just the latest Version. + */ + private byte[] loadContainerDef(final ImageVersionDetails selected) { + + + byte[] rawVirtConfig = null; + + try { + ByteBuffer byteBuffer = ThriftManager.getSatClient() + .getImageVersionVirtConfig(Session.getSatelliteToken(), selected.versionId); + rawVirtConfig = ThriftUtil.unwrapByteBuffer(byteBuffer); + + } catch (TException e) { + LOGGER.error("Failed to retrieve virtualizer config for image version " + "'" + + image.latestVersionId + ", see trace: ", e); + } + return rawVirtConfig; + } + /******************************************************************************** * * Helper triggering the actual thrift calls @@ -600,10 +748,28 @@ public class ImageDetailsWindow extends ImageDetailsWindowLayout implements UiFe return false; } } + + // TODO if image is container image upload also ContainerDefinition @link ContainerDefinition + if(image.getVirtId().equals(TConst.VIRT_DOCKER)) + saveContainerDefinition(); + changeMonitor.reset(); return true; } + private void saveContainerDefinition() { + containerDefinition.setDescription(txtContainerDescription.getText()); + + containerDefinition.getContainerMeta().setImageName(txtContainerImageName.getText()); + containerDefinition.getContainerMeta().setRunOptions(txtContainerRun.getText()); + containerDefinition.getContainerMeta().mountUserDir(chkContainerMountUserDir.isSelected()); + containerDefinition.getContainerMeta().setUserMountPath(txtContainerMountUserDir.getText()); + + // TODO do this only if the containerDefinition has changed. + saveContainerDef(image.versions.get(0).versionId,containerDefinition.toByteBuffer()); + LOGGER.info("Upload new DockerDefinition"); + } + /** * Helper to save the custom user permissions (those not included in * ImageBaseWrite). @@ -762,6 +928,37 @@ public class ImageDetailsWindow extends ImageDetailsWindowLayout implements UiFe if (virt != null) lblVirtualizer.setText(virt.getVirtName()); + + if(TConst.VIRT_DOCKER.equals(image.getVirtId())){ + lblVirtualizer.setText(TConst.VIRT_DOCKER); + showContainerTab(); + + containerDefinition = ContainerDefinition.fromByteArray(loadContainerDef(image.versions.get(0))); + + txtContainerDescription.setText(containerDefinition.getDescription()); + txtContainerImageName.setText(containerDefinition.getContainerMeta().getImageName()); + txtContainerRun.setText(containerDefinition.getContainerMeta().getRunOptions()); + chkContainerMountUserDir.setSelected(containerDefinition.getContainerMeta().mountUserDir()); + txtContainerMountUserDir.setEnabled(chkContainerMountUserDir.isSelected()); + txtContainerMountUserDir.setText(containerDefinition.getContainerMeta().getUserMountPath()); + + changeMonitor.add(txtContainerDescription). + addConstraint(new TextNotEmptyConstraint("Empty Dockerfile not allowed!")); + changeMonitor.add(txtContainerImageName) + .addConstraint(new TextNotEmptyConstraint("Empty Name not allowed!")); + changeMonitor.add(txtContainerRun) + .addConstraint(new TextNotEmptyConstraint("No Container Run Options provided!")); + // TODO txtContainerMountUserDir txt Field needs a Constraint, only when chkContainerMountUserDir is selected; + changeMonitor.add(txtContainerMountUserDir); + + chkContainerMountUserDir.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { + containerDefinition.getContainerMeta().mountUserDir(chkContainerMountUserDir.isSelected()); + txtContainerMountUserDir.setEnabled(chkContainerMountUserDir.isSelected()); + } + }); + } + // fill share mode combo, if not already done if (cboShareMode.getItemCount() == 0) { for (ShareMode mode : ShareMode.values()) { diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/ImageDetailsWindowLayout.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/ImageDetailsWindowLayout.java index 2792e9fd..9b86089a 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/ImageDetailsWindowLayout.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/ImageDetailsWindowLayout.java @@ -1,11 +1,8 @@ package org.openslx.dozmod.gui.window.layout; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Font; -import java.awt.Frame; -import java.awt.GridBagConstraints; -import java.awt.Insets; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import javax.swing.BorderFactory; import javax.swing.Box; @@ -20,6 +17,7 @@ import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTextArea; import javax.swing.JTextField; +import javax.swing.event.*; import org.openslx.bwlp.thrift.iface.OperatingSystem; import org.openslx.bwlp.thrift.iface.ShareMode; @@ -42,6 +40,14 @@ public abstract class ImageDetailsWindowLayout extends JDialog { protected final JTextField txtTitle; protected final JTextArea txtDescription; + private final JPanel pnlTabContainer; + protected final JTextArea txtContainerDescription; + protected final JCheckBox chkContainerMountUserDir; + protected final JTextField txtContainerRun; + protected final JTextField txtContainerImageName; + protected final JTextField txtContainerMountUserDir; + + protected QLabel lblError; protected final PersonLabel lblOwner; protected final JButton btnChangeOwner; @@ -268,6 +274,48 @@ public abstract class ImageDetailsWindowLayout extends JDialog { grdImagePermissionConfigurator.add(defaultPermissionPane).fill(true, false).expand(false, false); grdImagePermissionConfigurator.finish(false); + + + + /* ******************************************************************************* + * + * Container panel + * + ********************************************************************************/ + pnlTabContainer = new JPanel(); + txtContainerDescription = new JTextArea(); + + JPanel pnlContainerMeta = new JPanel(); + GridManager grdContainerMeta = new GridManager(pnlContainerMeta, 3, false, new Insets(8, 2, 8, 2)); + + grdContainerMeta.add(new QLabel("Image Name")); + txtContainerImageName = new JTextField(); + txtContainerImageName.setDocument(txtTitle.getDocument()); + grdContainerMeta.add(txtContainerImageName,2).fill(true,false); + grdContainerMeta.nextRow(); + + grdContainerMeta.add(new QLabel("Container Run Options")); + txtContainerRun = new JTextField(); + grdContainerMeta.add(txtContainerRun,2).fill(true,false); + grdContainerMeta.nextRow(); + + grdContainerMeta.add(new QLabel("Mount User Directory"),1); + chkContainerMountUserDir = new JCheckBox(); + txtContainerMountUserDir = new JTextField(); + grdContainerMeta.add(chkContainerMountUserDir,1); + grdContainerMeta.add(txtContainerMountUserDir,1).fill(true,false); + grdContainerMeta.finish(true); + + JScrollPane scrollableTextArea = new JScrollPane(txtContainerDescription); + GridManager grdContainer = new GridManager(pnlTabContainer, 1, false, new Insets(8, 2, 8, 2)); + grdContainer.add(scrollableTextArea,1).fill(true, true).expand(true,true); + grdContainer.add(pnlContainerMeta,1).fill(true, false) + .anchor(GridBagConstraints.FIRST_LINE_START); + grdContainer.add(Box.createVerticalGlue()).fill(true, true); + grdContainer.finish(false); + + + /* ******************************************************************************* * * Bottom panel for buttons @@ -299,4 +347,8 @@ public abstract class ImageDetailsWindowLayout extends JDialog { add(pnlTabs, BorderLayout.CENTER); add(pnlButtons, BorderLayout.PAGE_END); } + + protected void showContainerTab() { + pnlTabs.addTab("Container", pnlTabContainer); + } } diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/DockerfileUploadPage.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/DockerfileUploadPage.java index b4ea3e71..d009f43f 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/DockerfileUploadPage.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/DockerfileUploadPage.java @@ -169,14 +169,34 @@ public class DockerfileUploadPage extends ContainerUploadPageLayout { /** * ContainerMeta is used to store container specific information. - * An Object of this class will be converted with gson to json file. + * An object of this class will be serialized with gson to a json file. */ - private class ContainerMeta { + public class ContainerMeta { + + private String build_context_url; private String image_name; private String run_options; private Boolean mount_user_dir; + private String user_mount_path; + + public ContainerMeta() { + build_context_url = ""; + image_name = ""; + run_options = ""; + mount_user_dir = false; + user_mount_path = ""; + } + + public String getBuildContextUrl() { + return build_context_url; + } + + public void setBuildContextUrl(String buildContextUrl) { + this.build_context_url = build_context_url; + } + public String getRunOptions() { return run_options; } @@ -192,13 +212,23 @@ public class DockerfileUploadPage extends ContainerUploadPageLayout { public void setImageName(String image_name) { this.image_name = image_name; } - public Boolean getMountUserDir() { + + // TODO (Ralph) i dont like that naming + public Boolean mountUserDir() { return mount_user_dir; } - public void setMountUserDir(Boolean mountUserDir) { + public void mountUserDir(Boolean mountUserDir) { this.mount_user_dir = mountUserDir; } + + public String getUserMountPath() { + return user_mount_path; + } + + public void setUserMountPath(String userMountPath) { + user_mount_path = userMountPath; + } } private void reactOnUserInput() { |
