Skip to content

Commit

Permalink
Merge pull request #15 from abcdesktopio/dev
Browse files Browse the repository at this point in the history
appendpathtomounthomevolume
  • Loading branch information
alexandredevely authored Mar 21, 2024
2 parents 96cd0de + 5de807b commit baf7b29
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 65 deletions.
18 changes: 11 additions & 7 deletions oc/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,22 @@ def load_config(path, is_cp_file=False):
"""
load the config file from default configuration file 'od.config' if is_cp_file is True
load the config file PATH if is_cp_file is False """
cfg = None
cfg_logging = None

if is_cp_file is True:
logger.info("Reading cherrypy configuration section 'global/logging': path = %s", path)
cfg = Config(path)['global']['logging']
logger.info(f"Reading cherrypy configuration section 'global/logging': path = {path}")
config = Config(path)
if isinstance( config.get('global'), dict ):
cfg_logging = config.get('global').get('logging')
else:
cfg_logging = config.get('logging')
else:
logger.info("Reading json file: path = %s", path)
logger.info(f"Reading json file: path = {path}")
with open(path, encoding='UTF-8') as f:
cfg = json.decode(f.read())
cfg_logging = json.decode(f.read())

logger.debug("config = %s", repr(cfg))
return cfg
logger.debug(f"logging configuration : {cfg_logging}")
return cfg_logging


def init_logging(config_or_path, is_cp_file=True):
Expand Down
59 changes: 27 additions & 32 deletions oc/od/orchestrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -1160,9 +1160,11 @@ def build_volumes_home( self, authinfo:AuthInfo, userinfo:AuthUser, volume_type:
self.logger.debug(f"homedirectorytype is {homedirectorytype}")
subpath_name = oc.auth.namedlib.normalize_name( userinfo.userid )
self.logger.debug(f"subpath_name is {subpath_name}")
user_homedirectory = self.get_user_homedirectory(authinfo, userinfo)
self.logger.debug(f"user_homedirectory is {user_homedirectory}")

user_homedirectory = os.path.join( self.get_user_homedirectory(authinfo, userinfo),
oc.od.settings.desktop.get('appendpathtomounthomevolume','') )
user_homedirectory = os.path.normpath( user_homedirectory )
self.logger.debug( f"user_homedirectory mounts home volume to {user_homedirectory}" )

# set default value
# home is emptyDir
# cache is emptyDir Memory
Expand All @@ -1173,7 +1175,7 @@ def build_volumes_home( self, authinfo:AuthInfo, userinfo:AuthUser, volume_type:
# Take care if this is a pod application the .cache is empty
self.logger.debug( f"map ~/.cache to emptyDir Memory is {oc.od.settings.desktop.get('homedirdotcachetoemptydir')}" )
if oc.od.settings.desktop.get('homedirdotcachetoemptydir') is True :
dotcache_user_homedirectory = user_homedirectory + '/.cache'
dotcache_user_homedirectory = self.get_user_homedirectory(authinfo, userinfo) + '/.cache'
self.logger.debug( f"map {dotcache_user_homedirectory} to emptyDir medium Memory" )
volumes['cache'] = { 'name': 'cache', 'emptyDir': { 'medium': 'Memory', 'sizeLimit': '8Gi' } }
volumes_mount['cache'] = { 'name': 'cache', 'mountPath': dotcache_user_homedirectory }
Expand All @@ -1184,18 +1186,16 @@ def build_volumes_home( self, authinfo:AuthInfo, userinfo:AuthUser, volume_type:

# now ovewrite home values
if homedirectorytype == 'persistentVolumeClaim':

claimName = None # None is the default value, nothing to do

if isinstance( oc.od.settings.desktop['persistentvolumeclaim'], str):
# oc.od.settings.desktop['persistentvolumeclaim'] is the name of the PVC
# it must exists
# there is only one PVC for all users
# in this case, there is only one shared PVC for all users
# and it must already exists
if volume_type in [ 'pod_desktop', 'pod_application' ] :
claimName = oc.od.settings.desktop['persistentvolumeclaim']

if isinstance( oc.od.settings.desktop['persistentvolumeclaim'], dict):
# oc.od.settings.desktop['persistentvolumeclaim'] must be created by pyos
elif isinstance( oc.od.settings.desktop['persistentvolumeclaim'], dict):
# oc.od.settings.desktop['persistentvolumeclaim'] must be created by pyos
if volume_type in [ 'pod_desktop', 'pod_application' ] :
# create a pvc to store desktop volume
persistentvolume = copy.deepcopy( oc.od.settings.desktop['persistentvolume'] )
Expand Down Expand Up @@ -3024,6 +3024,7 @@ def getPodIPAddress( self, pod_name:str )->str:
IPAddress = myPod.status.pod_ip
except Exception as e:
self.logger.error( e )
self.logger.debug( f"pod_IPAddress is {IPAddress}" )
return IPAddress


Expand Down Expand Up @@ -3372,11 +3373,8 @@ def createdesktop(self, authinfo:AuthInfo, userinfo:AuthUser, **kwargs)-> ODDesk
timeout_seconds=oc.od.settings.desktop['K8S_CREATE_POD_TIMEOUT_SECONDS'],
field_selector=f'involvedObject.name={pod_name}'):

# safe type test event is a dict
if not isinstance(event, dict ): continue
# safe type test event object is a CoreV1Event
if not isinstance(event.get('object'), CoreV1Event ): continue

if not isinstance(event, dict ): continue # safe type test event is a dict
if not isinstance(event.get('object'), CoreV1Event ): continue # safe type test event object is a CoreV1Event
event_object = event.get('object')
self.logger.debug(f"{event_object.type} reason={event_object.reason} message={event_object.message}")
self.on_desktoplaunchprogress( f"b.{event_object.message}" )
Expand Down Expand Up @@ -3406,7 +3404,7 @@ def createdesktop(self, authinfo:AuthInfo, userinfo:AuthUser, **kwargs)-> ODDesk
if event_object.reason == 'Pulled':
pulled_counter = pulled_counter + 1
self.logger.debug( f"Event Pulled received pulled_counter={pulled_counter}")
# if all image are pulled pass
# if all images are pulled
self.logger.debug( f"counter pulled_counter={pulled_counter} expected_containers_len={expected_containers_len}")
if pulled_counter >= expected_containers_len :
self.logger.debug( f"counter pulled_counter={pulled_counter} >= expected_containers_len={expected_containers_len}")
Expand All @@ -3416,20 +3414,19 @@ def createdesktop(self, authinfo:AuthInfo, userinfo:AuthUser, **kwargs)-> ODDesk
self.on_desktoplaunchprogress(f"b.Your pod {pod.metadata.name} gets ip address {pod_IPAddress} from network plugin")
self.logger.debug( f"stop watching event list_namespaced_event for pod {pod.metadata.name} ")
w.stop()

elif event_object.reason == 'Started':
pod_IPAddress = self.getPodIPAddress( pod.metadata.name )
if isinstance( pod_IPAddress, str ):
self.logger.debug( f"{pod.metadata.name} has an ip address: {pod_IPAddress}")
self.on_desktoplaunchprogress(f"b.Your pod {pod.metadata.name} gets ip address {pod_IPAddress} from network plugin")
self.on_desktoplaunchprogress(f"b.Your pod {pod.metadata.name} gets ip address {pod_IPAddress} from network plugin")
self.logger.debug( f"counter pulled_counter={pulled_counter} expected_containers_len={expected_containers_len}")
if pulled_counter >= expected_containers_len :
self.logger.debug( f"counter pulled_counter={pulled_counter} >= expected_containers_len={expected_containers_len}")
self.logger.debug( f"stop watching event list_namespaced_event for pod {pod.metadata.name} ")
w.stop()

"""
c = self.getcontainerfromPod( self.graphicalcontainernameprefix, myPod )
c = self.getcontainerfromPod( self.graphicalcontainernameprefix, pod )
if isinstance( c, V1ContainerStatus ):
startedmsg = self.getPodStartedMessage(self.graphicalcontainernameprefix, myPod, event_object)
self.on_desktoplaunchprogress( startedmsg )
Expand All @@ -3443,7 +3440,7 @@ def createdesktop(self, authinfo:AuthInfo, userinfo:AuthUser, **kwargs)-> ODDesk
self.on_desktoplaunchprogress(f"b.Your pod gets event {event_object.message or event_object.reason}")
# fix for https://github.com/abcdesktopio/oc.user/issues/52
# this is not an error
# w.stop()
w.stop()
# return f"{event_object.reason} {event_object.message}"

else:
Expand All @@ -3466,12 +3463,9 @@ def createdesktop(self, authinfo:AuthInfo, userinfo:AuthUser, **kwargs)-> ODDesk
self.logger.error( f"event type is {type( event )}, and should be a dict, skipping event")
continue

# event dict must contain a type
event_type = event.get('type')
# event dict must contain a pod object
pod_event = event.get('object')
# if podevent type must be a V1Pod, we use kubeapi.list_namespaced_pod
if not isinstance( pod_event, V1Pod ): continue
event_type = event.get('type') # event dict must contain a type
pod_event = event.get('object') # event dict must contain a pod object
if not isinstance( pod_event, V1Pod ): continue # if podevent type must be a V1Pod
if not isinstance( pod_event.status, V1PodStatus ): continue
#
self.on_desktoplaunchprogress( f"b.Your {pod_event.kind.lower()} is {event_type.lower()}")
Expand Down Expand Up @@ -3504,7 +3498,7 @@ def createdesktop(self, authinfo:AuthInfo, userinfo:AuthUser, **kwargs)-> ODDesk
self.logger.error(f"The pod {pod_event.metadata.name} is in phase={pod_event.status.phase} stop watching" )
w.stop()

self.logger.debug(f"watch list_namespaced_pod created, the pod is no more in Pending phase phase={pod_event.status.phase}" )
self.logger.debug(f"watch list_namespaced_pod created, the pod is no more in Pending phase" )

# read pod again
self.logger.debug(f"read_namespaced_pod {pod_name} again" )
Expand Down Expand Up @@ -4481,15 +4475,16 @@ def create(self, myDesktop, app, authinfo, userinfo={}, userargs=None, **kwargs
break
if c.state.waiting.reason == 'Pulling':
send_previous_pulling_message = True
data['message'] = f"Installing {app.get('name')}, please wait"
data['message'] = f"{c.state.waiting.reason} {app.get('name')}, please wait"
self.orchestrator.notify_user( myDesktop, 'container', data )
elif c.state.waiting.reason == 'Pulled':
if send_previous_pulling_message is True:
data['message'] = f"{app.get('name')} is installed"
data['message'] = f"{app.get('name')} is {c.state.waiting.reason}"
self.orchestrator.notify_user( myDesktop, 'container', data )
else:
data['message'] = c.state.waiting.message
self.orchestrator.notify_user( myDesktop, 'container', data )
if send_previous_pulling_message is True:
data['message'] = c.state.waiting.reason
self.orchestrator.notify_user( myDesktop, 'container', data )

if event.get('type') == 'ERROR':
self.logger.error( f"{event.get('type')} object={type(event.get('object'))}")
Expand Down
31 changes: 24 additions & 7 deletions oc/od/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
# supported image format
ABCDESKTOP_IMAGE_FORMAT_RELEASE = '3.0'

defaultConfigurationFilename = 'od.config'

config = {} # use for application config and global config
gconfig = {} # use for global config

Expand Down Expand Up @@ -329,7 +327,7 @@ def init_desktop():
# default secret path
desktop['secretsrootdirectory'] = gconfig.get('desktop.secretsrootdirectory', '/var/secrets/')

desktop['release'] = gconfig.get('desktop.release', '3.1')
desktop['release'] = gconfig.get('desktop.release', '3.2')
#
# in release 3.1
# desktop['secretslocalaccount'] = gconfig.get('desktop.secretslocalaccount', '/etc/localaccount')
Expand All @@ -355,10 +353,13 @@ def init_desktop():
desktop['prestopexeccommand'] = gconfig.get('desktop.prestopexeccommand', [ "/bin/bash", "-c", "rm -rf ~/{*,.*}" ] )
desktop['persistentvolumeclaim'] = gconfig.get('desktop.persistentvolumeclaim') or gconfig.get('desktop.persistentvolumeclaimspec')
desktop['persistentvolume'] = gconfig.get('desktop.persistentvolume') or gconfig.get('desktop.persistentvolumespec')
desktop['persistentvolumeclaimforcesubpath'] = gconfig.get('desktop.persistentvolumeclaimforcesubpath',False)
desktop['homedirdotcachetoemptydir']= gconfig.get('desktop.homedirdotcachetoemptydir', False)
desktop['removepersistentvolume'] = gconfig.get('desktop.removepersistentvolume', False)
desktop['appendpathtomounthomevolume'] = gconfig.get('desktop.appendpathtomounthomevolume')
desktop['removepersistentvolumeclaim'] = gconfig.get('desktop.removepersistentvolumeclaim', False)
desktop['homedirdotcachetoemptydir']= gconfig.get('desktop.homedirdotcachetoemptydir', True)
desktop['persistentvolumeclaimforcesubpath'] = gconfig.get('desktop.persistentvolumeclaimforcesubpath',False)



desktop['K8S_BOUND_PVC_TIMEOUT_SECONDS'] = gconfig.get('K8S_BOUND_PVC_TIMEOUT_SECONDS', 60 )
desktop['K8S_BOUND_PVC_MAX_EVENT'] = gconfig.get('K8S_BOUND_PVC_MAX_EVENT', 5 )
Expand Down Expand Up @@ -685,15 +686,31 @@ def init_executeclass():
def get_default_appdict():
return dock


def get_configuration_file_name():
"""get_configuration_file_name
Returns:
str: name of the config file 'od.config' by default or read 'OD_CONFIG_PATH' os.environ
"""
configuration_file_name = os.environ.get('OD_CONFIG_PATH', 'od.config')
return configuration_file_name

def load_config():
global config
global gconfig

configpath = os.environ.get('OD_CONFIG_PATH', defaultConfigurationFilename)
configpath = get_configuration_file_name()
logger.info(f"Loading configuration file {configpath}")
try:
config = Config(configpath)
gconfig = config.get('global', {}) # = cherrypy.gconfig
if isinstance( config.get('global'), dict ):
logger.info(f"config file contains [global] entry (ini file format)")
gconfig = config.get('global', {}) # = cherrypy.gconfig
else:
logger.info(f"config file does not set [global] entry")
logger.info(f"config file is not a ini file format, use json")

except Exception as e:
logger.error(f"Failed to load configuration file {configpath} {e}")
exit(-1)
Expand Down
33 changes: 14 additions & 19 deletions od.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,12 @@
import oc.od.services as services

# Load logging config ASAP !
oc.logging.configure( config_or_path=oc.od.settings.defaultConfigurationFilename, is_cp_file=True)
oc.logging.configure( config_or_path=settings.get_configuration_file_name(), is_cp_file=True)
logger = logging.getLogger(__name__)

# define virtual path used
# app_virtual_path is for application
# img_virtual_path is for icon static file
app_virtual_path = '/API'
img_virtual_path = '/img'

# define each configration for API
# app_config is the core servive
# img_config is a dummy app used to serve icon static file
# app_config is the core service
# img_config is file service to send icon static file

# Allow (partial) case-insensivity in URLs
class APIDispatcher(Dispatcher):
Expand Down Expand Up @@ -112,12 +106,6 @@ def img_handle_404_application(status, message, traceback, version):
#
# img class to serve static files
# for example icon files for applications
@cherrypy.config(**{
'tools.staticdir.on' : True,
'tools.staticdir.dir': '/var/pyos/img', # relative path not allowed
'tools.allow.methods': [ 'GET' ], # HTTP GET only for images
'error_page.404' : img_handle_404_application
})
class IMG(object):
def __init__(self):
""" init IMG static files
Expand Down Expand Up @@ -232,14 +220,21 @@ def stop(self):
def run_server():
logger.info("Starting cherrypy service...")
# update config for cherrypy
cherrypy.config.update(settings.defaultConfigurationFilename)
settings.config['/']['request.dispatch'] = APIDispatcher() # can't be set with @cherrypy.config decorator
cherrypy.config.update(settings.get_configuration_file_name())
# cherrypy.config['/']
# APIConfig = { '/': { 'request.dispatch': APIDispatcher() } }
# settings.config['/']['request.dispatch'] = APIDispatcher() # can't be set with @cherrypy.config decorator
# set auth tools
cherrypy.tools.auth = services.services.auth
# set /API
cherrypy.tree.mount( API(settings.controllers), app_virtual_path, settings.config )
cherrypy.tree.mount( API(settings.controllers), '/API', settings.config )
# set /IMG
cherrypy.tree.mount(IMG(), img_virtual_path, config={} ) # no config for img, use class config
cherrypy.tree.mount( IMG(), '/img', config=
{ '/img': { 'tools.staticdir.on' : True,
'tools.staticdir.dir': '/var/pyos/img', # relative path not allowed
'tools.allow.methods': [ 'GET' ], # HTTP GET only for images
'error_page.404' : img_handle_404_application # overwrite 404 to default icon
} } )

odthread_watcher = ODCherryWatcher(cherrypy.engine)
odthread_watcher.subscribe()
Expand Down

0 comments on commit baf7b29

Please sign in to comment.