appy.shared: improved deployment of a Appy app (creation of a Zope instance is no more required; corresponding folders are created in standard unix locations: /etc for the config file, /var/log for logs, /var/lib for the database, /usr/bin for scripts that start and stop the instance). appy.gen: first draft of a migration script that allows to migrate data from Plone-dependent Appy apps (<= 0.7.1) to Ploneless Appy 0.8.0.
This commit is contained in:
parent
95a899f3de
commit
1275df5753
13 changed files with 351 additions and 161 deletions
|
@ -1,6 +1,6 @@
|
|||
# ------------------------------------------------------------------------------
|
||||
import os, os.path, subprocess, md5, shutil
|
||||
from appy.shared.utils import getOsTempFolder, FolderDeleter
|
||||
from appy.shared.utils import getOsTempFolder, FolderDeleter, cleanFolder
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
debianInfo = '''Package: python-appy%s
|
||||
|
@ -8,23 +8,77 @@ Version: %s
|
|||
Architecture: all
|
||||
Maintainer: Gaetan Delannay <gaetan.delannay@geezteem.com>
|
||||
Installed-Size: %d
|
||||
Depends: python (>= %s), python (<= %s)%s
|
||||
Depends: python (>= %s)%s
|
||||
Section: python
|
||||
Priority: optional
|
||||
Homepage: http://appyframework.org
|
||||
Description: Appy builds simple but complex web Python apps.
|
||||
'''
|
||||
appCtl = '''#!/usr/lib/zope2.12/bin/python
|
||||
import sys
|
||||
from appy.bin.zopectl import ZopeRunner
|
||||
args = ' '.join(sys.argv[1:])
|
||||
sys.argv = [sys.argv[0], '-C', '/etc/%s.conf', args]
|
||||
ZopeRunner().run()
|
||||
'''
|
||||
appRun = '''#!/bin/sh
|
||||
exec "/usr/lib/zope2.12/bin/runzope" -C "/etc/%s.conf" "$@"
|
||||
'''
|
||||
ooStart = '#!/bin/sh\nsoffice -invisible -headless -nofirststartwizard ' \
|
||||
'"-accept=socket,host=localhost,port=2002;urp;"&\n'
|
||||
zopeConf = '''# Zope configuration.
|
||||
%%define INSTANCE %s
|
||||
%%define DATA %s
|
||||
%%define LOG %s
|
||||
%%define HTTPPORT 8080
|
||||
%%define ZOPE_USER zope
|
||||
|
||||
instancehome $INSTANCE
|
||||
effective-user $ZOPE_USER
|
||||
%s
|
||||
<eventlog>
|
||||
level info
|
||||
<logfile>
|
||||
path $LOG/event.log
|
||||
level info
|
||||
</logfile>
|
||||
</eventlog>
|
||||
<logger access>
|
||||
level WARN
|
||||
<logfile>
|
||||
path $LOG/Z2.log
|
||||
format %%(message)s
|
||||
</logfile>
|
||||
</logger>
|
||||
<http-server>
|
||||
address $HTTPPORT
|
||||
</http-server>
|
||||
<zodb_db main>
|
||||
<filestorage>
|
||||
path $DATA/Data.fs
|
||||
</filestorage>
|
||||
mount-point /
|
||||
</zodb_db>
|
||||
<zodb_db temporary>
|
||||
<temporarystorage>
|
||||
name temporary storage for sessioning
|
||||
</temporarystorage>
|
||||
mount-point /temp_folder
|
||||
container-class Products.TemporaryFolder.TemporaryContainer
|
||||
</zodb_db>
|
||||
'''
|
||||
|
||||
class Debianizer:
|
||||
'''This class allows to produce a Debian package from a Python (Appy)
|
||||
package.'''
|
||||
|
||||
def __init__(self, app, out, appVersion='0.1.0',
|
||||
pythonVersions=('2.6', '2.7'),
|
||||
pythonVersions=('2.6',),
|
||||
depends=('zope2.12', 'openoffice.org', 'imagemagick')):
|
||||
# app is the path to the Python package to Debianize.
|
||||
self.app = app
|
||||
self.appName = os.path.basename(app)
|
||||
self.appNameLower = self.appName.lower()
|
||||
# out is the folder where the Debian package will be generated.
|
||||
self.out = out
|
||||
# What is the version number for this app ?
|
||||
|
@ -33,6 +87,8 @@ class Debianizer:
|
|||
self.pythonVersions = pythonVersions
|
||||
# Debian package dependencies
|
||||
self.depends = depends
|
||||
# Zope 2.12 requires Python 2.6
|
||||
if 'zope2.12' in depends: self.pythonVersions = ('2.6',)
|
||||
|
||||
def run(self):
|
||||
'''Generates the Debian package.'''
|
||||
|
@ -49,19 +105,74 @@ class Debianizer:
|
|||
for version in self.pythonVersions:
|
||||
libFolder = j(srcFolder, 'python%s' % version)
|
||||
os.makedirs(libFolder)
|
||||
shutil.copytree(self.app, j(libFolder, self.appName))
|
||||
# Create data.tar.gz based on it.
|
||||
os.chdir(debFolder)
|
||||
os.system('tar czvf data.tar.gz ./usr')
|
||||
destFolder = j(libFolder, self.appName)
|
||||
shutil.copytree(self.app, destFolder)
|
||||
# Clean dest folder (.svn/.bzr files)
|
||||
cleanFolder(destFolder, folders=('.svn', '.bzr'))
|
||||
# When packaging Appy itself, everything is in /usr/lib/pythonX. When
|
||||
# packaging an Appy app, we will generate more files for creating a
|
||||
# running instance.
|
||||
if self.appName != 'appy':
|
||||
# Create the folders that will collectively represent the deployed
|
||||
# Zope instance.
|
||||
binFolder = j(debFolder, 'usr', 'bin')
|
||||
os.makedirs(binFolder)
|
||||
# <app>ctl
|
||||
name = '%s/%sctl' % (binFolder, self.appNameLower)
|
||||
f = file(name, 'w')
|
||||
f.write(appCtl % self.appNameLower)
|
||||
os.chmod(name, 0744) # Make it executable by owner.
|
||||
f.close()
|
||||
# <app>run
|
||||
name = '%s/%srun' % (binFolder, self.appNameLower)
|
||||
f = file(name, 'w')
|
||||
f.write(appRun % self.appNameLower)
|
||||
os.chmod(name, 0744) # Make it executable by owner.
|
||||
f.close()
|
||||
# startoo
|
||||
name = '%s/startoo' % binFolder
|
||||
f = file(name, 'w')
|
||||
f.write(ooStart)
|
||||
f.close()
|
||||
os.chmod(name, 0744) # Make it executable by owner.
|
||||
# /var/lib/<app> (will store Data.fs, lock files, etc)
|
||||
varLibFolder = j(debFolder, 'var', 'lib', self.appNameLower)
|
||||
os.makedirs(varLibFolder)
|
||||
f = file('%s/README' % varLibFolder, 'w')
|
||||
f.write('This folder stores the %s database.\n' % self.appName)
|
||||
f.close()
|
||||
# /var/log/<app> (will store event.log and Z2.log)
|
||||
varLogFolder = j(debFolder, 'var', 'log', self.appNameLower)
|
||||
os.makedirs(varLogFolder)
|
||||
f = file('%s/README' % varLogFolder, 'w')
|
||||
f.write('This folder stores the log files for %s.\n' % self.appName)
|
||||
f.close()
|
||||
# /etc/<app>.conf (Zope configuration file)
|
||||
etcFolder = j(debFolder, 'etc')
|
||||
os.makedirs(etcFolder)
|
||||
name = '%s/%s.conf' % (etcFolder, self.appNameLower)
|
||||
n = self.appNameLower
|
||||
f = file(name, 'w')
|
||||
productsFolder = '/usr/lib/python%s/%s/zope' % \
|
||||
(self.pythonVersions[0], self.appName)
|
||||
f.write(zopeConf % ('/var/lib/%s' % n, '/var/lib/%s' % n,
|
||||
'/var/log/%s' % n,
|
||||
'products %s\n' % productsFolder))
|
||||
f.close()
|
||||
# Get the size of the app, in Kb.
|
||||
cmd = subprocess.Popen(['du', '-b', '-s', 'usr'],stdout=subprocess.PIPE)
|
||||
os.chdir(tempFolder)
|
||||
cmd = subprocess.Popen(['du', '-b', '-s', 'debian'],
|
||||
stdout=subprocess.PIPE)
|
||||
size = int(int(cmd.stdout.read().split()[0])/1024.0)
|
||||
os.chdir(debFolder)
|
||||
# Create data.tar.gz based on it.
|
||||
os.system('tar czvf data.tar.gz *')
|
||||
# Create the control file
|
||||
f = file('control', 'w')
|
||||
nameSuffix = ''
|
||||
dependencies = []
|
||||
if self.appName != 'appy':
|
||||
nameSuffix = '-%s' % self.appName.lower()
|
||||
nameSuffix = '-%s' % self.appNameLower
|
||||
dependencies.append('python-appy')
|
||||
if self.depends:
|
||||
for d in self.depends: dependencies.append(d)
|
||||
|
@ -69,51 +180,48 @@ class Debianizer:
|
|||
if dependencies:
|
||||
depends = ', ' + ', '.join(dependencies)
|
||||
f.write(debianInfo % (nameSuffix, self.appVersion, size,
|
||||
self.pythonVersions[0], self.pythonVersions[1],
|
||||
depends))
|
||||
self.pythonVersions[0], depends))
|
||||
f.close()
|
||||
# Create md5sum file
|
||||
f = file('md5sums', 'w')
|
||||
for dir, dirnames, filenames in os.walk('usr'):
|
||||
for name in filenames:
|
||||
m = md5.new()
|
||||
pathName = j(dir, name)
|
||||
currentFile = file(pathName, 'rb')
|
||||
while True:
|
||||
data = currentFile.read(8096)
|
||||
if not data:
|
||||
break
|
||||
m.update(data)
|
||||
currentFile.close()
|
||||
# Add the md5 sum to the file
|
||||
f.write('%s %s\n' % (m.hexdigest(), pathName))
|
||||
toWalk = ['usr']
|
||||
if self.appName != 'appy':
|
||||
toWalk += ['etc', 'var']
|
||||
for folderToWalk in toWalk:
|
||||
for dir, dirnames, filenames in os.walk(folderToWalk):
|
||||
for name in filenames:
|
||||
m = md5.new()
|
||||
pathName = j(dir, name)
|
||||
currentFile = file(pathName, 'rb')
|
||||
while True:
|
||||
data = currentFile.read(8096)
|
||||
if not data:
|
||||
break
|
||||
m.update(data)
|
||||
currentFile.close()
|
||||
# Add the md5 sum to the file
|
||||
f.write('%s %s\n' % (m.hexdigest(), pathName))
|
||||
f.close()
|
||||
# Create postinst, a script that will:
|
||||
# - bytecompile Python files after the Debian install
|
||||
# - create a Zope instance (excepted if we are installing Appy itself).
|
||||
# - change ownership of some files if required
|
||||
f = file('postinst', 'w')
|
||||
content = '#!/bin/sh\nset -e\n'
|
||||
for version in self.pythonVersions:
|
||||
bin = '/usr/bin/python%s' % version
|
||||
lib = '/usr/lib/python%s' % version
|
||||
cmds = ' %s -m compileall -q %s/%s 2> /dev/null\n' % (bin, lib,
|
||||
self.appName)
|
||||
if self.appName != 'appy':
|
||||
inst = '/home/zope/%sInstance' % self.appName
|
||||
cmds += ' if [ -e %s ]\n then\n' % inst
|
||||
# If the Zope instance already exists, simply restart it.
|
||||
cmds += ' %s/bin/zopectl restart\n else\n' % inst
|
||||
# Else, create a Zope instance in the home of user "zope".
|
||||
cmds += ' %s %s/appy/bin/new.py zope /usr/lib/zope2.12 ' \
|
||||
'%s\n' % (bin, lib, inst)
|
||||
# Within this instance, create a symlink to the Zope product
|
||||
cmds += ' ln -s %s/%s/zope %s/Products/%s\n' % \
|
||||
(lib, self.appName, inst, self.appName)
|
||||
# Launch the instance
|
||||
cmds += ' %s/bin/zopectl start\n' % inst
|
||||
# Launch OpenOffice in server mode
|
||||
cmds += ' %s/bin/startoo\n fi\n' % inst
|
||||
cmds = ' %s -m compileall -q %s/%s 2> /dev/null\n' % (bin, lib,
|
||||
self.appName)
|
||||
content += 'if [ -e %s ]\nthen\n%sfi\n' % (bin, cmds)
|
||||
if self.appName != 'appy':
|
||||
# Allow user "zope", that runs the Zope instance, to write the
|
||||
# database and log files.
|
||||
content += 'chown -R zope:root /var/lib/%s\n' % self.appNameLower
|
||||
content += 'chown -R zope:root /var/log/%s\n' % self.appNameLower
|
||||
# (re-)start the app
|
||||
content += '%sctl restart\n' % self.appNameLower
|
||||
# (re-)start oo
|
||||
content += 'startoo\n'
|
||||
f.write(content)
|
||||
f.close()
|
||||
# Create prerm, a script that will remove all pyc files before removing
|
||||
|
|
|
@ -36,18 +36,28 @@ class FolderDeleter:
|
|||
|
||||
# ------------------------------------------------------------------------------
|
||||
extsToClean = ('.pyc', '.pyo', '.fsz', '.deltafsz', '.dat', '.log')
|
||||
def cleanFolder(folder, exts=extsToClean, verbose=False):
|
||||
def cleanFolder(folder, exts=extsToClean, folders=(), verbose=False):
|
||||
'''This function allows to remove, in p_folder and subfolders, any file
|
||||
whose extension is in p_exts.'''
|
||||
whose extension is in p_exts, and any folder whose name is in
|
||||
p_folders.'''
|
||||
if verbose: print 'Cleaning folder', folder, '...'
|
||||
# Remove files with an extension listed in exts
|
||||
for root, dirs, files in os.walk(folder):
|
||||
for fileName in files:
|
||||
ext = os.path.splitext(fileName)[1]
|
||||
if (ext in exts) or ext.endswith('~'):
|
||||
fileToRemove = os.path.join(root, fileName)
|
||||
if verbose: print 'Removing %s...' % fileToRemove
|
||||
os.remove(fileToRemove)
|
||||
# Remove files with an extension listed in p_exts
|
||||
if exts:
|
||||
for root, dirs, files in os.walk(folder):
|
||||
for fileName in files:
|
||||
ext = os.path.splitext(fileName)[1]
|
||||
if (ext in exts) or ext.endswith('~'):
|
||||
fileToRemove = os.path.join(root, fileName)
|
||||
if verbose: print 'Removing file %s...' % fileToRemove
|
||||
os.remove(fileToRemove)
|
||||
# Remove folders whose names are in p_folders.
|
||||
if folders:
|
||||
for root, dirs, files in os.walk(folder):
|
||||
for folderName in dirs:
|
||||
if folderName in folders:
|
||||
toDelete = os.path.join(root, folderName)
|
||||
if verbose: print 'Removing folder %s...' % toDelete
|
||||
FolderDeleter.delete(toDelete)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
def copyFolder(source, dest, cleanDest=False):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue