Skip to content

Commit

Permalink
GUI: Look for cloudfuse CLI in path (#167)
Browse files Browse the repository at this point in the history
* Use system path resolution to find cloudfuse CLI (no more './cloudfuse')
* Use the same mount logic for Windows & Linux
* Set allow-other to false by default because on Linux, /etc/fuse.conf does not set user_allow_other by default.
* If the cloudfuse CLI is not in the path, look for it in the current directory
* no camel case needed for cloudfuse
  • Loading branch information
foodprocessor committed Apr 4, 2024
1 parent b1df80b commit 9f5d781
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 91 deletions.
2 changes: 1 addition & 1 deletion gui/common_qt_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def setComponentSettings(self):
self.settings.setValue('foreground',False)

# Common
self.settings.setValue('allow-other',True)
self.settings.setValue('allow-other',False)
self.settings.setValue('read-only',False)
self.settings.setValue('nonempty',False)
self.settings.setValue('restricted-characters-windows',False) # not exposed
Expand Down
171 changes: 83 additions & 88 deletions gui/mountPrimaryWindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@
import os
import time
import yaml
from shutil import which

# Import QT libraries
from PySide6.QtCore import Qt, QSettings
from PySide6 import QtWidgets, QtGui, QtCore
from PySide6.QtWidgets import QMainWindow

# Import the custom class created with QtDesigner
# Import the custom class created with QtDesigner
from ui_mountPrimaryWindow import Ui_primaryFUSEwindow
from s3_config_common import s3SettingsWidget
from azure_config_common import azureSettingsWidget
Expand All @@ -42,6 +43,17 @@

bucketOptions = ['s3storage', 'azstorage']
mountTargetComponent = 3
cloudfuseCli = 'cloudfuse'
mountDirSuffix = ''
if platform == 'win32':
# on Windows, the cli command ends in '.exe'
cloudfuseCli += '.exe'
# on Windows, the mound directory must not exist before mounting,
# so name a non-existent subdirectory of the user-chosen path
mountDirSuffix = 'cloudfuse'
# if cloudfuse is not in the path, look for it in the current directory
if which(cloudfuseCli) is None:
cloudfuseCli = './' + cloudfuseCli

class FUSEWindow(QMainWindow, Ui_primaryFUSEwindow):
def __init__(self):
Expand All @@ -62,9 +74,8 @@ def __init__(self):
# Allow anything BUT Nul
# Note: Different versions of Python don't like the embedded null character, send in the raw string instead
self.lineEdit_mountPoint.setValidator(QtGui.QRegularExpressionValidator(r'^[^\0]*$',self))


# Set up the signals for all the interactable intities
# Set up the signals for all the interactive entities
self.button_browse.clicked.connect(self.getFileDirInput)
self.button_config.clicked.connect(self.showSettingsWidget)
self.button_mount.clicked.connect(self.mountBucket)
Expand Down Expand Up @@ -98,7 +109,7 @@ def initMountPoint(self):
except:
# Nothing in the settings for mountDir, leave mountPoint blank
return

def updateMountPointInSettings(self):
try:
directory = str(self.lineEdit_mountPoint.text())
Expand All @@ -109,10 +120,9 @@ def updateMountPointInSettings(self):

# Define the slots that will be triggered when the signals in Qt are activated

# There are unique settings per bucket selected for the pipeline,
# There are unique settings per bucket selected for the pipeline,
# so we must use different widgets to show the different settings
def showSettingsWidget(self):

targetIndex = self.dropDown_bucketSelect.currentIndex()
if bucketOptions[targetIndex] == 's3storage':
self.settings = s3SettingsWidget()
Expand All @@ -136,10 +146,7 @@ def showAboutQtPage(self):

# Display the custom dialog box for the cloudfuse 'about' page.
def showAboutCloudFusePage(self):
if platform == "win32":
commandParts = ['cloudfuse.exe', '--version']
else:
commandParts = ['./cloudfuse', '--version']
commandParts = [cloudfuseCli, '--version']
(stdOut, stdErr, exitCode, executableFound) = self.runCommand(commandParts)

if not executableFound:
Expand All @@ -148,100 +155,90 @@ def showAboutCloudFusePage(self):
cloudfuseVersion = stdOut
else:
cloudfuseVersion = 'Cloudfuse version not found'

self.page = aboutPage(cloudfuseVersion)
self.page.show()

def showUnderConstructionPage(self):
self.page = underConstruction()
self.page.show()


def mountBucket(self):

# get mount directory
try:
directory = str(self.lineEdit_mountPoint.text())
except ValueError as e:
self.addOutputText(f"Invalid mount path: {str(e)}")
return
directory = os.path.join(directory, mountDirSuffix)
# get config path
configPath = os.path.join(widgetFuncs.getWorkingDir(self), 'config.yaml')

# on Windows, the mount directory should not exist (yet)
if platform == "win32":
# Windows mount has a quirk where the folder shouldn't exist yet,
# add CloudFuse at the end of the directory
directory = os.path.join(directory,'cloudFuse')

# make sure the mount directory doesn't already exist
if os.path.exists(directory):
self.addOutputText(f"Directory {directory} already exists! Aborting new mount.")
self.errorMessageBox(f"Error: Cloudfuse needs to create the directory {directory}, but it already exists!")
return

# do a dry run to validate options and credentials
commandParts = ['cloudfuse.exe', 'mount', directory, f'--config-file={configPath}', '--dry-run']
(stdOut, stdErr, exitCode, executableFound) = self.runCommand(commandParts)
if not executableFound:
self.addOutputText("cloudfuse.exe not found! Is it installed?")
self.errorMessageBox("Error running cloudfuse CLI - Please re-install Cloudfuse.")
return

if exitCode != 0:
self.addOutputText(stdErr)
self.errorMessageBox("Mount failed: " + stdErr)
return

if stdOut != "":
self.addOutputText(stdOut)

# now actually mount
commandParts = ['cloudfuse.exe', 'mount', directory, f'--config-file={configPath}']
(stdOut, stdErr, exitCode, executableFound) = self.runCommand(commandParts)
if not executableFound:
self.addOutputText("cloudfuse.exe not found! Is it installed?")
self.errorMessageBox("Error running cloudfuse CLI - Please re-install Cloudfuse.")
return

if exitCode != 0:
self.addOutputText(stdErr)
if stdErr.find("mount path exists") != -1:
self.errorMessageBox("This container is already mounted at this directory.")
return

if stdOut != "":
self.addOutputText(stdOut)

# wait for mount, then check that mount succeeded by verifying that the mount directory exists
self.addOutputText("Mount command successfully sent to Windows service.\nVerifying mount success...")
def verifyMountSuccess():
if not os.path.exists(directory):
self.addOutputText(f"Failed to create mount directory {directory}")
self.errorMessageBox("Mount failed. Please check error logs.")
else:
self.addOutputText("Successfully mounted container")
QtCore.QTimer.singleShot(4000, verifyMountSuccess)
else:
commandParts = ['./cloudfuse', 'mount', directory, f'--config-file={configPath}']
(stdOut, stdErr, exitCode, executableFound) = self.runCommand(commandParts)
if exitCode != 0:
self.addOutputText(f"Error mounting container: {stdErr}")

# do a dry run to validate options and credentials
commandParts = [cloudfuseCli, 'mount', directory, f'--config-file={configPath}', '--dry-run']
(stdOut, stdErr, exitCode, executableFound) = self.runCommand(commandParts)
if not executableFound:
self.addOutputText("cloudfuse.exe not found! Is it installed?")
self.errorMessageBox("Error running cloudfuse CLI - Please re-install Cloudfuse.")
return

if exitCode != 0:
self.addOutputText(stdErr)
self.errorMessageBox("Mount failed: " + stdErr)
return

if stdOut != "":
self.addOutputText(stdOut)

# now actually mount
commandParts = [cloudfuseCli, 'mount', directory, f'--config-file={configPath}']
(stdOut, stdErr, exitCode, executableFound) = self.runCommand(commandParts)
if not executableFound:
self.addOutputText("cloudfuse.exe not found! Is it installed?")
self.errorMessageBox("Error running cloudfuse CLI - Please re-install Cloudfuse.")
return

if exitCode != 0:
self.addOutputText(f"Error mounting container: {stdErr}")
if stdErr.find("mount path exists") != -1:
self.errorMessageBox("This container is already mounted at this directory.")
else:
self.errorMessageBox(f"Error mounting container - check the settings and try again\n{stdErr}")
return

self.addOutputText("Successfully mounted container\n")
return

if stdOut != "":
self.addOutputText(stdOut)

# wait for mount, then check that mount succeeded by verifying that the mount directory exists
self.addOutputText("Verifying mount success...")
def verifyMountSuccess():
if platform == 'win32':
success = os.path.exists(directory)
else:
success = os.path.ismount(directory)
if not success:
self.addOutputText(f"Failed to create mount directory {directory}")
self.errorMessageBox("Mount failed. Please check error logs.")
else:
self.addOutputText("Successfully mounted container")
QtCore.QTimer.singleShot(4000, verifyMountSuccess)

def unmountBucket(self):
directory = str(self.lineEdit_mountPoint.text())
commandParts = []
# TODO: properly handle unmount. This is relying on the line_edit not being changed by the user.

if platform == "win32":
# for windows, 'cloudfuse' was added to the directory so add it back in for umount
directory = os.path.join(directory, 'cloudFuse')
commandParts = "cloudfuse.exe unmount".split()
else:
commandParts = "./cloudfuse unmount --lazy".split()
commandParts.append(directory)

directory = os.path.join(directory, mountDirSuffix)
commandParts = [cloudfuseCli, "unmount", directory]
if platform != "win32":
commandParts.append("--lazy")

(stdOut, stdErr, exitCode, executableFound) = self.runCommand(commandParts)
if not executableFound:
self.addOutputText("cloudfuse.exe not found! Is it installed?")
Expand All @@ -252,11 +249,9 @@ def unmountBucket(self):
else:
self.addOutputText(f"Successfully unmounted container {stdErr}")



# This function reads in the config file, modifies the components section, then writes the config file back
def modifyPipeline(self):

self.addOutputText("Validating configuration...")
# Update the pipeline/components before mounting the target

Expand All @@ -272,8 +267,8 @@ def modifyPipeline(self):
f"Could not read the config file in {workingDir}. Consider going through the settings for selected target.",
"Could not read config file")
return
# Modify the components (pipeline) in the config file.

# Modify the components (pipeline) in the config file.
# If the components are not present, there's a chance the configs are wrong. Notify user.

components = configs.get('components')
Expand All @@ -285,15 +280,15 @@ def modifyPipeline(self):
f"The components is missing in {workingDir}/config.yaml. Consider Going through the settings to create one.",
"Components in config missing")
return
# Write the config file with the modified components

# Write the config file with the modified components
try:
with open(workingDir+'/config.yaml','w') as file:
yaml.safe_dump(configs,file)
except:
self.errorMessageBox(f"Could not modify {workingDir}/config.yaml.", "Could not modify config file")
return

# run command and return tuple:
# (stdOut, stdErr, exitCode, executableFound)
def runCommand(self, commandParts):
Expand All @@ -311,12 +306,12 @@ def runCommand(self, commandParts):
return ('', '', -1, False)
except PermissionError:
return ('', '', -1, False)

def addOutputText(self, textString):
self.textEdit_output.setText(f"{self.textEdit_output.toPlainText()}{textString}\n")
self.textEdit_output.repaint()
self.textEdit_output.moveCursor(QtGui.QTextCursor.End)

def errorMessageBox(self, messageString, titleString="Error"):
msg = QtWidgets.QMessageBox()
# Get the user's attention by popping open a new window
Expand Down
4 changes: 2 additions & 2 deletions gui/s3_config_common.ui
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
<item>
<widget class="QCheckBox" name="checkBox_daemonForeground">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Run CloudFuse in the foreground of background. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Run Cloudfuse in the foreground of background. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Run in foreground</string>
Expand Down Expand Up @@ -140,7 +140,7 @@
<item>
<widget class="QLabel" name="label_2">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Set the pipeline mode for CloudFuse&lt;/p&gt;&lt;p&gt;Choose either File caching or Streaming&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Set the pipeline mode for Cloudfuse&lt;/p&gt;&lt;p&gt;Choose either File caching or Streaming&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Mode</string>
Expand Down

0 comments on commit 9f5d781

Please sign in to comment.