package org.openslx.dozmod.gui.wizard.page; import org.apache.commons.io.FileUtils; import org.apache.log4j.Logger; import org.openslx.bwlp.thrift.iface.ImageDetailsRead; import org.openslx.dozmod.Config; import org.openslx.dozmod.gui.helper.QFileChooser; import org.openslx.dozmod.gui.helper.TextChangeListener; import org.openslx.dozmod.gui.wizard.Wizard; import org.openslx.dozmod.gui.wizard.layout.ContainerUploadPageLayout; import org.openslx.dozmod.model.ContainerBuildContextMethod; import org.openslx.dozmod.model.ContainerDefinition; import org.openslx.dozmod.model.ContainerMeta; import org.openslx.dozmod.state.UploadWizardState; import org.openslx.dozmod.thrift.cache.MetaDataCache; import org.openslx.util.vm.DockerMetaDataDummy; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.filechooser.FileFilter; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ContainerUploadPage extends ContainerUploadPageLayout { // TODO: Add a Instruction for the new Container-Feature in bwLehrpool. // TODO: Add link to instructions for Docker-Intetragtion at https://www.bwlehrpool.de/doku.php private final Logger LOGGER = Logger.getLogger(ContainerUploadPage.class); /** * Page for uploading an imagefile * * @param wizard */ private final UploadWizardState state; private final ImageDetailsRead existingImage; private final ContainerDefinition containerDefinition; public ContainerUploadPage(Wizard wizard, final UploadWizardState state, ContainerDefinition containerDefinition) { super(wizard); this.containerDefinition = containerDefinition; this.state = state; canComeBack = false; existingImage = null; // HACK set dummy os state.selectedOs = MetaDataCache.getOsById(18); 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. public ContainerUploadPage(Wizard wizard, UploadWizardState uploadWizardState, ImageDetailsRead imageDetailsRead) { super(wizard); state = uploadWizardState; existingImage = imageDetailsRead; // TODO fix this! containerDefinition = null; lblImageName.setEnabled(existingImage == null); txtImageName.setEnabled(existingImage == null); txtInfoText.setVisible(existingImage == null); state.name = imageDetailsRead.imageName; state.defaultPermissions = imageDetailsRead.getDefaultPermissions(); state.description = imageDetailsRead.getDescription(); state.detectedOs = MetaDataCache.getOsById(imageDetailsRead.getOsId()); state.selectedOs = MetaDataCache.getOsById(imageDetailsRead.getOsId()); state.tags = imageDetailsRead.getTags(); state.defaultPermissions = imageDetailsRead.getUserPermissions(); state.uuid = imageDetailsRead.getImageBaseId(); init(); } private void init() { this.txtImageFile.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { browseFile(); } }); this.btnBrowseForImage.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { browseFile(); } }); txtImageName.getDocument().addDocumentListener(new TextChangeListener() { @Override public void changed() { reactOnUserInput(); } }); txtContainerRun.getDocument().addDocumentListener(new TextChangeListener() { @Override public void changed() { reactOnUserInput(); } }); tpInput.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { reactOnUserInput(); } }); txtGitRepo.getDocument().addDocumentListener(new TextChangeListener() { @Override public void changed() { reactOnUserInput(); } }); btnBrowseForImage.requestFocus(); } private void browseFile() { QFileChooser fc = new QFileChooser(Config.getUploadPath(), false); fc.setAcceptAllFileFilterUsed(false); fc.addChoosableFileFilter(new DockerfileFilter()); int action = fc.showOpenDialog(getDialog()); File file = fc.getSelectedFile(); if (action != JFileChooser.APPROVE_OPTION || file == null) return; txtImageFile.setText(file.getAbsolutePath()); if (existingImage == null) txtImageName.setText(file.getParentFile().getName()); else 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()) { LOGGER.info("Upload also an created Image"); state.diskFile = imageTarFile; } else { state.diskFile = getDummyFile(); } Config.setUploadPath(file.getParent()); reactOnUserInput(); } private File getDummyFile() { String configDir = Config.getPath(); File zeroFile = new File(configDir, "ZERO-FILE"); // create a temp file with dummy content, // because vmchooser relies on an existing image after choosing a lecture. // TODO change behavior in vmchooser to allow start lectures without images. try { if (!zeroFile.exists()) { zeroFile.createNewFile(); FileUtils.writeStringToFile(zeroFile, "ZERO\n", "UTF-8"); } } catch (IOException e) { e.printStackTrace(); LOGGER.error("Could not create a dummy file.", e); } LOGGER.info("Use dummy file"); return zeroFile; } private void reactOnUserInput() { boolean completed = checkUserInput(); if (completed) setDescription("Container definition finished"); setPageComplete(completed); } private boolean checkUserInput() { ContainerBuildContextMethod method = getBuildContextMethod(); switch (method) { case FILE: if (txtImageFile.getText() == null || txtImageFile.getText().isEmpty()) { setWarningMessage("No Container Recipe provided!"); return false; } break; case GIT_REPOSITORY: if (txtGitRepo.getText() == null || txtGitRepo.getText().isEmpty()) { setWarningMessage("No Git Repository provided!"); return false; } break; } if (txtImageName.getText() == null || txtImageName.getText().isEmpty()) { setWarningMessage("Set proper Image Name"); return false; } if (txtContainerRun.getText() == null || txtContainerRun.getText().isEmpty()) { setWarningMessage("set container run options"); return false; } return true; } private DockerMetaDataDummy createVmMeta() { ContainerMeta containerMeta = containerDefinition.getContainerMeta(); containerMeta.setBuildContextMethod(getBuildContextMethod().ordinal()); containerMeta.setImageName(txtImageName.getText()); containerMeta.setRunOptions(txtContainerRun.getText()); switch (containerDefinition.getBuildContextMethod()) { case FILE: containerDefinition.setContainerRecipe(state.descriptionFile); break; case GIT_REPOSITORY: containerMeta.setBuildContextUrl(txtGitRepo.getText()); state.diskFile = getDummyFile(); state.descriptionFile = getDummyFile(); break; } return containerDefinition.createVmMeta(); } @Override protected boolean wantNextOrFinish() { state.name = existingImage != null ? existingImage.getImageName() : txtImageName.getText(); state.meta = createVmMeta(); return true; } private static class DockerfileFilter extends FileFilter { @Override public boolean accept(File f) { Pattern p = Pattern.compile("[Dd]ockerfile"); Matcher m = p.matcher(f.getName()); boolean accept = false; if (f.isFile() && m.matches()) accept = true; return accept; } @Override public String getDescription() { return "Dockerfile"; } } }