From 15436e3dad49bf6ead28fa0e667bbd38f42bbe96 Mon Sep 17 00:00:00 2001 From: ralph isenmann Date: Fri, 28 Aug 2020 13:44:13 +0200 Subject: [client] Allow to upload dockerfile and containerMeta * Introduce the ContainerMeta, which contains container specific information. * Upload dockerfile and containerMeta object (as json) as a single tar.gz-file --- dozentenmodul/pom.xml | 5 + .../wizard/layout/ContainerUploadPageLayout.java | 92 ++++++++++++ .../gui/wizard/page/DockerfileUploadPage.java | 161 ++++++++++++++++++--- 3 files changed, 234 insertions(+), 24 deletions(-) create mode 100644 dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/ContainerUploadPageLayout.java diff --git a/dozentenmodul/pom.xml b/dozentenmodul/pom.xml index 1b7c6154..e4cfa85f 100755 --- a/dozentenmodul/pom.xml +++ b/dozentenmodul/pom.xml @@ -220,6 +220,11 @@ [2.0,] compile + + org.kamranzafar + jtar + 2.3 + 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 new file mode 100644 index 00000000..26425813 --- /dev/null +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/ContainerUploadPageLayout.java @@ -0,0 +1,92 @@ +package org.openslx.dozmod.gui.wizard.layout; + +import org.openslx.dozmod.gui.control.QLabel; +import org.openslx.dozmod.gui.helper.GridManager; +import org.openslx.dozmod.gui.wizard.Wizard; +import org.openslx.dozmod.gui.wizard.WizardPage; + +import javax.swing.*; +import java.awt.event.KeyEvent; + +public class ContainerUploadPageLayout extends WizardPage { + + protected final JTextField txtImageName; + protected final JButton btnBrowseForImage; + protected final JTextField txtImageFile; + protected final JCheckBox chkLicenseRestricted; + protected final QLabel lblImageName; + protected final JTextArea txtInfoText; + + protected final JTextField txtContainerRun; + protected final QLabel lblContainerRun; + protected final QLabel lblMountUserDir; + protected final JCheckBox chkMountUserDir; + + // TODO set proper Text labels + /** + * Page for uploading an imagefile + */ + public ContainerUploadPageLayout(Wizard wizard) { + + super(wizard, "Neue VM anlegen"); + setDescription("Bitte wählen Sie eine Virtuelle Maschine zum Hochladen aus."); + GridManager grid = new GridManager(this, 3, false); + + // -- Browse for VM -- + QLabel imageFileCaption = new QLabel("Virtuelle Maschine"); + txtImageFile = new JTextField(); + txtImageFile.setEditable(false); + btnBrowseForImage = new JButton("Durchsuchen"); + btnBrowseForImage.setMnemonic(KeyEvent.VK_B); + grid.add(imageFileCaption); + grid.add(txtImageFile).fill(true, false).expand(true, false); + grid.add(btnBrowseForImage); + grid.nextRow(); + + lblImageName = new QLabel("Name"); + txtImageName = new JTextField(); + grid.add(lblImageName); + grid.add(txtImageName, 2, 1).fill(true, false).expand(true, false); + grid.nextRow(); + + // -- Software license changed - shown only in UploadWizard -- + chkLicenseRestricted = new JCheckBox("enthält lizenzpflichtige Software"); + chkLicenseRestricted.setVisible(false); + grid.skip(); + grid.add(chkLicenseRestricted, 2, 1).fill(false, false).expand(true, false); + grid.nextRow(); + + grid.add(new JSeparator(),3); + grid.nextRow(); + + lblContainerRun = new QLabel("Container Start Optionen"); + lblContainerRun.setToolTipText("Geben Sie die Container Run Optionen an (Port, Name, Env,...)"); + txtContainerRun = new JTextField(); + lblMountUserDir = new QLabel("Mount User Directory"); + chkMountUserDir = new JCheckBox(); + grid.add(lblContainerRun); + grid.add(txtContainerRun, 2, 1).fill(true, false).expand(true, false); + grid.add(lblMountUserDir); + grid.add(chkMountUserDir,2,1).fill(false, false).expand(true, false); + grid.nextRow(); + + + + + grid.add(Box.createVerticalGlue(), 3).expand(true, true); + + txtInfoText = new JTextArea(); + txtInfoText.setBorder(BorderFactory.createTitledBorder("Hinweis")); + txtInfoText.setLineWrap(true); + txtInfoText.setWrapStyleWord(true); + txtInfoText.setEditable(false); + txtInfoText.setFocusable(false); + txtInfoText.setOpaque(false); + txtInfoText.setText("Container Info Text"); + grid.add(txtInfoText, 3).fill(true, false).expand(true, false); + grid.nextRow(); + + grid.finish(false); + } + +} 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 324b3112..a90329a1 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 @@ -1,25 +1,32 @@ package org.openslx.dozmod.gui.wizard.page; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import org.apache.commons.io.FileUtils; import org.apache.log4j.Logger; +import org.kamranzafar.jtar.TarEntry; +import org.kamranzafar.jtar.TarHeader; +import org.kamranzafar.jtar.TarOutputStream; import org.openslx.bwlp.thrift.iface.ImageDetailsRead; import org.openslx.dozmod.Config; import org.openslx.dozmod.gui.Gui; import org.openslx.dozmod.gui.helper.*; import org.openslx.dozmod.gui.wizard.Wizard; -import org.openslx.dozmod.gui.wizard.layout.ImageUploadPageLayout; +import org.openslx.dozmod.gui.wizard.layout.ContainerUploadPageLayout; import org.openslx.dozmod.state.UploadWizardState; import org.openslx.dozmod.thrift.*; import org.openslx.dozmod.thrift.cache.MetaDataCache; +import org.openslx.util.Util; import org.openslx.util.vm.DockerMetaDataDummy; import javax.swing.*; import javax.swing.filechooser.FileFilter; import java.awt.event.*; -import java.io.File; -import java.io.IOException; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.zip.GZIPOutputStream; -public class DockerfileUploadPage extends ImageUploadPageLayout { +public class DockerfileUploadPage extends ContainerUploadPageLayout { private final static Logger LOGGER = Logger.getLogger(DockerfileUploadPage.class); @@ -30,22 +37,31 @@ public class DockerfileUploadPage extends ImageUploadPageLayout { */ private final UploadWizardState state; private final ImageDetailsRead existingImage; + private final ContainerMeta containerMeta; - public DockerfileUploadPage(Wizard wizard, final UploadWizardState state) { + private File dockerfile; + + public DockerfileUploadPage(Wizard wizard, final UploadWizardState state_) { super(wizard); - this.canComeBack = false; - this.state = state; + canComeBack = false; + state = state_; existingImage = null; + // HACK set dummy os + state.selectedOs = MetaDataCache.getOsById(18); + containerMeta = new ContainerMeta(); init(); } - public DockerfileUploadPage(Wizard wizard, UploadWizardState state, ImageDetailsRead image) { + public DockerfileUploadPage(Wizard wizard, UploadWizardState state_, ImageDetailsRead image) { super(wizard); - this.state = state; - this.existingImage = image; + state = state_; + existingImage = image; + // TODO Restore ContainerMeta to show Configuration (and Dockerfile) in ImageDetailsWindow, + // Cannot be done, because vmx not downloaded! + containerMeta = new ContainerMeta(); lblImageName.setEnabled(existingImage == null); txtImageName.setEnabled(existingImage == null); @@ -66,7 +82,6 @@ public class DockerfileUploadPage extends ImageUploadPageLayout { } private void init() { - this.txtImageFile.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { browseFile(); @@ -80,17 +95,18 @@ public class DockerfileUploadPage extends ImageUploadPageLayout { txtImageName.getDocument().addDocumentListener(new TextChangeListener() { @Override public void changed() { - String imageName = txtImageName.getText(); - if (imageName.equals("")) - setPageComplete(false); - else - setPageComplete(true); - state.name = imageName; + reactOnUserInput(); + } + }); + txtContainerRun.getDocument().addDocumentListener(new TextChangeListener() { + @Override public void changed() { + reactOnUserInput(); } }); btnBrowseForImage.requestFocus(); txtInfoText.setText("Many Text"); + } private void browseFile() { @@ -114,11 +130,11 @@ public class DockerfileUploadPage extends ImageUploadPageLayout { else txtImageName.setText(existingImage.getImageName()); + dockerfile = file; state.descriptionFile = file; // TESTING: Upload also a prematurely created image (tar) String imageName = file.getParentFile().getName(); - String tarExt = ".tar"; - File imageTarFile = new File(file.getParentFile(),imageName.concat(tarExt)); + File imageTarFile = new File(file.getParentFile(),imageName.concat(".tar")); if(imageTarFile.exists()) { LOGGER.info("Upload also an created Image"); state.diskFile = imageTarFile; @@ -133,10 +149,8 @@ public class DockerfileUploadPage extends ImageUploadPageLayout { } } - state.meta = new DockerMetaDataDummy(MetaDataCache.getOperatingSystems(),file); - Config.setUploadPath(file.getParent()); - setPageComplete(true); + reactOnUserInput(); } private class DockerfileFilter extends FileFilter { @@ -154,12 +168,112 @@ public class DockerfileUploadPage extends ImageUploadPageLayout { } } + /** + * ContainerMeta is used to store container specific information. + * An Object of this class will be converted with gson to json file. + */ + private class ContainerMeta { + + private String image_name; + private String run_options; + private Boolean mount_user_dir; + + public String getRunOptions() { + return run_options; + } + + public void setRunOptions(String run_options) { + this.run_options = run_options; + } + + public String getImageName() { + return image_name; + } + + public void setImageName(String image_name) { + this.image_name = image_name; + } + public Boolean getMountUserDir() { + return mount_user_dir; + } + + public void setMountUserDir(Boolean mountUserDir) { + this.mount_user_dir = mountUserDir; + } + } + + private void reactOnUserInput() { + boolean completed = true; + + state.name = txtImageName.getText(); + containerMeta.setImageName(state.name); + if (state.name == null || state.name.isEmpty()) { + completed = false; + setWarningMessage("Set proper Image Name"); + } + + containerMeta.setRunOptions(txtContainerRun.getText()); + if (containerMeta.getRunOptions() == null || containerMeta.getRunOptions().isEmpty()) { + completed = false; + setWarningMessage("set container run options"); + } + + if (completed) + setDescription("Container definition finished"); + setPageComplete(completed); + } + + /** + * This Method takes the user provided dockerfile and a ContainerMeta object + * and packages those in temporally .tar.gz-file. + * This file is then attached to a DockerMetaDataDummy object as VMX. + * This ensures that dockerfile and ContainerMeta are kept in the server and made available to clients when a lecture + * starts with attached containers. + */ + private void createContainerInfo() { + + try { + File tarFile = File.createTempFile("bwlp-container-info_",".tar.gz"); + TarOutputStream output = new TarOutputStream(new GZIPOutputStream(new FileOutputStream(tarFile))); + + BufferedInputStream bis = new BufferedInputStream(new FileInputStream(dockerfile)); + byte[] data = new byte[(int) dockerfile.length()]; + bis.read(data); + + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + tarPutFile(output,"container_meta.json",gson.toJson(containerMeta)); + tarPutFile(output,"dockerfile", data); + + output.close(); + + state.meta = new DockerMetaDataDummy(MetaDataCache.getOperatingSystems(),tarFile); + } catch (IOException e) { + LOGGER.warn("Error writing to tar file", e); + } + } + + 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); + } + + @Override protected boolean wantNextOrFinish() { // are we creating a new image? then either: // get the image name either auto filled by VmwareMetaData or by user // get the image name from the image we are uploading a new version of state.name = existingImage != null ? existingImage.getImageName() : txtImageName.getText(); - + createContainerInfo(); // -- create image to get uuid -- // if (existingImage == null) { if (state.uuid == null) { @@ -191,6 +305,5 @@ public class DockerfileUploadPage extends ImageUploadPageLayout { // Start the hash check now state.upload.startHashing(); return true; - } } -- cgit v1.2.3-55-g7522