Commit 314206ec authored by Nicolas Wavrant's avatar Nicolas Wavrant

Merge branch 'slaprunner-paas'

parents 4eb93526 22c18547
from datetime import datetime import argparse
import csv import csv
import feedparser import feedparser
import httplib # To avoid magic numbers
import io import io
import socket
import json import json
import time
import math import math
import httplib # To avoid magic numbers
import argparse
import os import os
import socket
import sys
import time
from datetime import datetime
from hashlib import sha512 from hashlib import sha512
from atomize import Entry from atomize import Entry
...@@ -19,6 +20,9 @@ from flask import abort ...@@ -19,6 +20,9 @@ from flask import abort
from flask import request from flask import request
app = Flask(__name__) app = Flask(__name__)
# csv entries can be very large, increase limit.
csv.field_size_limit(sys.maxsize)
@app.route('/get/<feed>') @app.route('/get/<feed>')
def get_feed(feed): def get_feed(feed):
global app global app
......
...@@ -47,3 +47,17 @@ For reference: How-to deploy the whole test system ...@@ -47,3 +47,17 @@ For reference: How-to deploy the whole test system
Note: the slapos nodes are currently deployed using slapos-in-partition. Note: the slapos nodes are currently deployed using slapos-in-partition.
Note: you have to manually kill -10 the erp5testnode process to start deployment of test because it doesn't know when SR installation is finished. Note: you have to manually kill -10 the erp5testnode process to start deployment of test because it doesn't know when SR installation is finished.
Note: you have to manually run slapos-node-software --all on the slapos nodes if you are developping the SR you are testing. Note: you have to manually run slapos-node-software --all on the slapos nodes if you are developping the SR you are testing.
------------
STANDALONE TESTS
Here is an example on how to deploy standalone tests on the webrunner, which means without using erp5.
1/ Deploy a SlapRunner software instance using the type test.
2/ In slapos.org, you should tell on which server you want to deploy your instances. You can adapt to your case the parameter.xml above. For the first time, you can deploy all the instances on the same node, it will run the tests faster, and it will be easier to debug :
<?xml version='1.0' encoding='utf-8'?>
<instance>
<parameter id="_">{"cluster": {"-sla-0-computer_guid": "COMP-XXXX", "-sla-1-computer_guid": "COMP-XXXX", "-sla-2-computer_guid": "COMP-XXXX"}}</parameter>
</instance>
3/ Then go to the root instance folder : it is the one who has only "runStandaloneResiliencyTestSuite" in its bin folder.
4/ Run ./bin/runStandaloneResiliencyTestSuite and wait :) it would return "success" or "failure"
...@@ -32,6 +32,7 @@ import slapos.slap ...@@ -32,6 +32,7 @@ import slapos.slap
import logging import logging
import time import time
import os
class ResiliencyTestSuite(object): class ResiliencyTestSuite(object):
""" """
...@@ -104,6 +105,14 @@ class ResiliencyTestSuite(object): ...@@ -104,6 +105,14 @@ class ResiliencyTestSuite(object):
""" """
raise NotImplementedError('Overload me, I am an abstract method.') raise NotImplementedError('Overload me, I am an abstract method.')
def deleteTimestamp():
"""
XXX-Nicolas delete .timestamp in test partition to force the full processing
by slapgrid, to force the good parameters to be passed to the instances of the tree
"""
home = os.getenv('HOME')
timestamp = os.path.join(home, '.timestamp')
os.remove(timestamp)
def _getPartitionParameterDict(self): def _getPartitionParameterDict(self):
""" """
...@@ -115,6 +124,7 @@ class ResiliencyTestSuite(object): ...@@ -115,6 +124,7 @@ class ResiliencyTestSuite(object):
software_type='resilient', software_type='resilient',
partition_reference=self.root_instance_name partition_reference=self.root_instance_name
).getConnectionParameterDict() ).getConnectionParameterDict()
self.deleteTimestamp()
def _returnNewInstanceParameter(self, parameter_key, old_parameter_value): def _returnNewInstanceParameter(self, parameter_key, old_parameter_value):
""" """
...@@ -126,8 +136,8 @@ class ResiliencyTestSuite(object): ...@@ -126,8 +136,8 @@ class ResiliencyTestSuite(object):
new_parameter_value = None new_parameter_value = None
while not new_parameter_value or new_parameter_value == 'None' or new_parameter_value == old_parameter_value: while not new_parameter_value or new_parameter_value == 'None' or new_parameter_value == old_parameter_value:
self.logger.info('Not ready yet. SlapOS says new parameter value is %s' % new_parameter_value) self.logger.info('Not ready yet. SlapOS says new parameter value is %s' % new_parameter_value)
time.sleep(60)
new_parameter_value = self._getPartitionParameterDict().get(parameter_key, None) new_parameter_value = self._getPartitionParameterDict().get(parameter_key, None)
time.sleep(120)
self.logger.info('New parameter value of instance is %s' % new_parameter_value) self.logger.info('New parameter value of instance is %s' % new_parameter_value)
return new_parameter_value return new_parameter_value
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
from .resiliencytestsuite import ResiliencyTestSuite from .resiliencytestsuite import ResiliencyTestSuite
import base64
import cookielib import cookielib
import random import random
import string import string
...@@ -63,6 +64,7 @@ class SlaprunnerTestSuite(ResiliencyTestSuite): ...@@ -63,6 +64,7 @@ class SlaprunnerTestSuite(ResiliencyTestSuite):
300 300
) )
def _connectToSlaprunner(self, resource, data=None): def _connectToSlaprunner(self, resource, data=None):
""" """
Utility. Utility.
...@@ -81,10 +83,8 @@ class SlaprunnerTestSuite(ResiliencyTestSuite): ...@@ -81,10 +83,8 @@ class SlaprunnerTestSuite(ResiliencyTestSuite):
def _login(self): def _login(self):
self.logger.debug('Logging in...') self.logger.debug('Logging in...')
self._connectToSlaprunner('doLogin', data='clogin=%s&cpwd=%s' % ( b64string = base64.encodestring('%s:%s' % (self.slaprunner_user, self.slaprunner_password))[:-1]
self.slaprunner_user, self._opener_director.addheaders = [('Authorization', 'Basic %s'%b64string)]
self.slaprunner_password)
)
def _retrieveInstanceLogFile(self): def _retrieveInstanceLogFile(self):
""" """
...@@ -112,10 +112,17 @@ class SlaprunnerTestSuite(ResiliencyTestSuite): ...@@ -112,10 +112,17 @@ class SlaprunnerTestSuite(ResiliencyTestSuite):
return data return data
def _waitForSoftwareBuild(self): def _waitForSoftwareBuild(self):
while self._connectToSlaprunner(resource='slapgridResult', data='position=0&log=').find('"software": true') is not -1: #while self._connectToSlaprunner(resource='slapgridResult', data='position=0&log=').find('"software": true') is not -1:
self.logger.info('Software release is still building. Sleeping...') # self.logger.info('Software release is still building. Sleeping...')
time.sleep(15) # time.sleep(15)
self.logger.info('Software Release has been built / is no longer building.') #self.logger.info('Software Release has been built / is no longer building.')
try:
while self._connectToSlaprunner(resource='isSRReady') != "1":
self.logger.info('Software release is still building. Sleeping...')
time.sleep(15)
except (NotHttpOkException, urllib2.HTTPError):
# The nginx frontend might timeout before software release is finished.
self._waitForSoftwareBuild()
def _buildSoftwareRelease(self): def _buildSoftwareRelease(self):
self.logger.info('Building the Software Release...') self.logger.info('Building the Software Release...')
...@@ -199,6 +206,7 @@ class SlaprunnerTestSuite(ResiliencyTestSuite): ...@@ -199,6 +206,7 @@ class SlaprunnerTestSuite(ResiliencyTestSuite):
self._openSoftwareRelease('helloworld') self._openSoftwareRelease('helloworld')
self._buildSoftwareRelease() self._buildSoftwareRelease()
time.sleep(15)
self._deployInstance() self._deployInstance()
self.data = self._retrieveInstanceLogFile() self.data = self._retrieveInstanceLogFile()
...@@ -219,9 +227,7 @@ class SlaprunnerTestSuite(ResiliencyTestSuite): ...@@ -219,9 +227,7 @@ class SlaprunnerTestSuite(ResiliencyTestSuite):
) )
self._login() self._login()
self._waitForSoftwareBuild() self._waitForSoftwareBuild()
# XXX: in theory, it should be done automatically by slaprunner. time.sleep(15)
# In practice, it is still too dangerous for ERP5 instances.
self._deployInstance()
new_data = self._retrieveInstanceLogFile() new_data = self._retrieveInstanceLogFile()
if new_data == self.data: if new_data == self.data:
......
...@@ -6,51 +6,12 @@ import ConfigParser ...@@ -6,51 +6,12 @@ import ConfigParser
import datetime import datetime
import logging import logging
import logging.handlers import logging.handlers
from optparse import OptionParser, Option
import os import os
import slapos.runner.process from slapos.htpasswd import HtpasswdFile
from slapos.runner.process import setHandler
import sys import sys
from slapos.runner.utils import runInstanceWithLock from slapos.runner.utils import runInstanceWithLock
from slapos.runner.views import *
class Parser(OptionParser):
"""
Parse all arguments.
"""
def __init__(self, usage=None, version=None):
"""
Initialize all possible options.
"""
option_list = [
Option("-l", "--log_file",
help="The path to the log file used by the script.",
type=str),
Option("-v", "--verbose",
default=False,
action="store_true",
help="Verbose output."),
Option("-c", "--console",
default=False,
action="store_true",
help="Console output."),
Option("-d", "--debug",
default=False,
action="store_true",
help="Debug mode."),
]
OptionParser.__init__(self, usage=usage, version=version,
option_list=option_list)
def check_args(self):
"""
Check arguments
"""
(options, args) = self.parse_args()
if len(args) != 1:
self.error("Incorrect number of arguments")
return options, args[0]
class Config: class Config:
...@@ -61,19 +22,15 @@ class Config: ...@@ -61,19 +22,15 @@ class Config:
self.logger = None self.logger = None
self.verbose = None self.verbose = None
def setConfig(self, option_dict, configuration_file_path): def setConfig(self):
""" """
Set options given by parameters. Set options given by parameters.
""" """
self.configuration_file_path = os.path.abspath(configuration_file_path) self.configuration_file_path = os.path.abspath(os.getenv('RUNNER_CONFIG'))
# Set options parameters
for option, value in option_dict.__dict__.items():
setattr(self, option, value)
# Load configuration file # Load configuration file
configuration_parser = ConfigParser.SafeConfigParser() configuration_parser = ConfigParser.SafeConfigParser()
configuration_parser.read(configuration_file_path) configuration_parser.read(self.configuration_file_path)
# Merges the arguments and configuration
for section in ("slaprunner", "slapos", "slapproxy", "slapformat", for section in ("slaprunner", "slapos", "slapproxy", "slapformat",
"sshkeys_authority", "gitclient", "cloud9_IDE"): "sshkeys_authority", "gitclient", "cloud9_IDE"):
...@@ -88,17 +45,17 @@ class Config: ...@@ -88,17 +45,17 @@ class Config:
if self.console: if self.console:
self.logger.addHandler(logging.StreamHandler()) self.logger.addHandler(logging.StreamHandler())
if self.log_file: self.log_file = self.log_dir + '/slaprunner.log'
if not os.path.isdir(os.path.dirname(self.log_file)): if not os.path.isdir(os.path.dirname(self.log_file)):
# fallback to console only if directory for logs does not exists and # fallback to console only if directory for logs does not exists and
# continue to run # continue to run
raise ValueError('Please create directory %r to store %r log file' % ( raise ValueError('Please create directory %r to store %r log file' % (
os.path.dirname(self.log_file), self.log_file)) os.path.dirname(self.log_file), self.log_file))
else: else:
file_handler = logging.FileHandler(self.log_file) file_handler = logging.FileHandler(self.log_file)
file_handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")) file_handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
self.logger.addHandler(file_handler) self.logger.addHandler(file_handler)
self.logger.info('Configured logging to file %r' % self.log_file) self.logger.info('Configured logging to file %r' % self.log_file)
self.logger.info("Started.") self.logger.info("Started.")
self.logger.info(os.environ['PATH']) self.logger.info(os.environ['PATH'])
...@@ -107,30 +64,33 @@ class Config: ...@@ -107,30 +64,33 @@ class Config:
self.logger.debug("Verbose mode enabled.") self.logger.debug("Verbose mode enabled.")
def run(): def checkHtpasswd(config):
"Run default configuration." """XXX:set for backward compatiblity
usage = "usage: %s [options] CONFIGURATION_FILE" % sys.argv[0] create a htpassword if etc/.users exist"""
user = os.path.join(config['etc_dir'], '.users')
try: htpasswdfile = os.path.join(config['etc_dir'], '.htpasswd')
# Parse arguments if os.path.exists(user) and not os.path.exists(htpasswdfile):
config = Config() data = open(user).read().strip().split(';')
config.setConfig(*Parser(usage=usage).check_args()) htpasswd = HtpasswdFile(htpasswdfile, create=True)
htpasswd.update(data[0], data[1])
htpasswd.save()
else:
return
if os.getuid() == 0:
# avoid mistakes (mainly in development mode)
raise Exception('Do not run SlapRunner as root.')
serve(config) def run():
return_code = 0 "Run default configuration."
except SystemExit as err: # Parse arguments
# Catch exception raise by optparse config = Config()
return_code = err config.setConfig()
sys.exit(return_code) if os.getuid() == 0:
# avoid mistakes (mainly in development mode)
raise Exception('Do not run SlapRunner as root.')
serve(config)
def serve(config): def serve(config):
from views import app
from werkzeug.contrib.fixers import ProxyFix from werkzeug.contrib.fixers import ProxyFix
workdir = os.path.join(config.runner_workdir, 'project') workdir = os.path.join(config.runner_workdir, 'project')
software_link = os.path.join(config.runner_workdir, 'softwareLink') software_link = os.path.join(config.runner_workdir, 'softwareLink')
...@@ -145,14 +105,15 @@ def serve(config): ...@@ -145,14 +105,15 @@ def serve(config):
SECRET_KEY=os.urandom(24), SECRET_KEY=os.urandom(24),
PERMANENT_SESSION_LIFETIME=datetime.timedelta(days=31), PERMANENT_SESSION_LIFETIME=datetime.timedelta(days=31),
) )
checkHtpasswd(app.config)
if not os.path.exists(workdir): if not os.path.exists(workdir):
os.mkdir(workdir) os.mkdir(workdir)
if not os.path.exists(software_link): if not os.path.exists(software_link):
os.mkdir(software_link) os.mkdir(software_link)
slapos.runner.process.setHandler() setHandler()
config.logger.info('Running slapgrid...') config.logger.info('Running slapgrid...')
runInstanceWithLock(app.config) runInstanceWithLock(app.config)
config.logger.info('Done.') config.logger.info('Done.')
app.wsgi_app = ProxyFix(app.wsgi_app) app.wsgi_app = ProxyFix(app.wsgi_app)
app.run(host=config.runner_host, port=int(config.runner_port),
debug=config.debug, threaded=True) run()
...@@ -121,36 +121,40 @@ def getDiff(project): ...@@ -121,36 +121,40 @@ def getDiff(project):
result = safeResult(str(e)) result = safeResult(str(e))
return result return result
def gitCommit(project, msg):
"""Commit changes for the specified repository
Args:
project: directory of the local repository
msg: commit message"""
code = 0
json = ""
repo = Repo(project)
if repo.is_dirty:
git = repo.git
#add file to be commited
files = repo.untracked_files
for f in files:
git.add(f)
#Commit all modified and untracked files
git.commit('-a', '-m', msg)
else:
code = 1
json = "Nothing to be commited"
return jsonify(code=code, result=json)
def gitPush(project, msg): def gitPush(project):
"""Commit and Push changes for the specified repository """Push changes for the specified repository
Args: Args:
project: directory of the local repository project: directory of the local repository
msg: commit message""" msg: commit message"""
code = 0 code = 0
json = "" json = ""
undo_commit = False
try: try:
repo = Repo(project) #push changes to repo
if repo.is_dirty: current_branch = repo.active_branch.name
git = repo.git git.push('origin', current_branch)
current_branch = repo.active_branch.name code = 1
#add file to be commited
files = repo.untracked_files
for f in files:
git.add(f)
#Commit all modified and untracked files
git.commit('-a', '-m', msg)
undo_commit = True
#push changes to repo
git.push('origin', current_branch)
code = 1
else:
json = "Nothing to commit"
code = 1
except Exception as e: except Exception as e:
if undo_commit:
git.reset("HEAD~") # undo previous commit
json = safeResult(str(e)) json = safeResult(str(e))
return jsonify(code=code, result=json) return jsonify(code=code, result=json)
......
...@@ -2,6 +2,14 @@ ...@@ -2,6 +2,14 @@
# vim: set et sts=2: # vim: set et sts=2:
# pylint: disable-msg=W0311,C0301,C0103,C0111,R0904 # pylint: disable-msg=W0311,C0301,C0103,C0111,R0904
#############################################
# !!! Attention !!!
# You now have to comment the last line
# in __init__py, wich starts the functiun
# run() in order to start the tests,
# or it will NOT work
#############################################
import argparse import argparse
import ConfigParser import ConfigParser
import datetime import datetime
...@@ -13,10 +21,12 @@ import time ...@@ -13,10 +21,12 @@ import time
import unittest import unittest
from slapos.runner.utils import (getProfilePath, getSession, isInstanceRunning, from slapos.runner.utils import (getProfilePath, getSession, isInstanceRunning,
isSoftwareRunning, startProxy) isSoftwareRunning, startProxy,
isSoftwareReleaseReady)
from slapos.runner.process import killRunningProcess, isRunning from slapos.runner.process import killRunningProcess, isRunning
from slapos.runner import views from slapos.runner import views
import slapos.slap import slapos.slap
from slapos.htpasswd import HtpasswdFile
#Helpers #Helpers
...@@ -63,7 +73,6 @@ class SlaprunnerTestCase(unittest.TestCase): ...@@ -63,7 +73,6 @@ class SlaprunnerTestCase(unittest.TestCase):
self.project = 'slapos' # Default project name self.project = 'slapos' # Default project name
self.template = 'template.cfg' self.template = 'template.cfg'
self.partitionPrefix = 'slappart' self.partitionPrefix = 'slappart'
self.slaposBuildout = "1.6.0-dev-SlapOS-010"
#create slaprunner configuration #create slaprunner configuration
config = Config() config = Config()
config.setConfig() config.setConfig()
...@@ -110,7 +119,6 @@ class SlaprunnerTestCase(unittest.TestCase): ...@@ -110,7 +119,6 @@ class SlaprunnerTestCase(unittest.TestCase):
shutil.rmtree(self.app.config['instance_root']) shutil.rmtree(self.app.config['instance_root'])
if os.path.exists(self.app.config['software_link']): if os.path.exists(self.app.config['software_link']):
shutil.rmtree(self.app.config['software_link']) shutil.rmtree(self.app.config['software_link'])
self.logout()
#Stop process #Stop process
killRunningProcess('slapproxy', recursive=True) killRunningProcess('slapproxy', recursive=True)
killRunningProcess('slapgrid-cp', recursive=True) killRunningProcess('slapgrid-cp', recursive=True)
...@@ -128,26 +136,11 @@ class SlaprunnerTestCase(unittest.TestCase): ...@@ -128,26 +136,11 @@ class SlaprunnerTestCase(unittest.TestCase):
), ),
follow_redirects=True) follow_redirects=True)
def login(self, username, password):
"""Helper for Login method"""
return self.app.post('/doLogin',
data=dict(
clogin=username,
cpwd=password
),
follow_redirects=True)
def setAccount(self): def setAccount(self):
"""Initialize user account and log user in""" """Initialize user account and log user in"""
response = loadJson(self.configAccount(self.users[0], self.users[1], response = loadJson(self.configAccount(self.users[0], self.users[1],
self.users[2], self.users[3], self.rcode)) self.users[2], self.users[3], self.rcode))
response2 = loadJson(self.login(self.users[0], self.users[1]))
self.assertEqual(response['result'], "") self.assertEqual(response['result'], "")
self.assertEqual(response2['result'], "")
def logout(self):
"""Helper for Logout current user"""
return self.app.get('/dologout', follow_redirects=True)
def updateAccount(self, newaccount, rcode): def updateAccount(self, newaccount, rcode):
"""Helper for update user account data""" """Helper for update user account data"""
...@@ -186,8 +179,7 @@ class SlaprunnerTestCase(unittest.TestCase): ...@@ -186,8 +179,7 @@ class SlaprunnerTestCase(unittest.TestCase):
sr += "find-links += http://www.nexedi.org/static/packages/source/slapos.buildout/\n\n" sr += "find-links += http://www.nexedi.org/static/packages/source/slapos.buildout/\n\n"
sr += "[networkcache]\ndownload-cache-url = http://www.shacache.org/shacache" sr += "[networkcache]\ndownload-cache-url = http://www.shacache.org/shacache"
sr += "\ndownload-dir-url = http://www.shacache.org/shadir\n\n" sr += "\ndownload-dir-url = http://www.shacache.org/shadir\n\n"
sr += "[command]\nrecipe = zc.recipe.egg\neggs = plone.recipe.command\n\n" sr += "[command]\nrecipe = zc.recipe.egg\neggs = plone.recipe.command\n zc.buildout\n\n"
sr += "[versions]\nzc.buildout = %s\n" % self.slaposBuildout
os.mkdir(testSoftware) os.mkdir(testSoftware)
open(os.path.join(testSoftware, self.app.config['software_profile']), open(os.path.join(testSoftware, self.app.config['software_profile']),
'w').write(sr) 'w').write(sr)
...@@ -216,13 +208,6 @@ class SlaprunnerTestCase(unittest.TestCase): ...@@ -216,13 +208,6 @@ class SlaprunnerTestCase(unittest.TestCase):
"""Kill slapproxy process""" """Kill slapproxy process"""
killRunningProcess('slapproxy', recursive=True) killRunningProcess('slapproxy', recursive=True)
#Begin test case here
def test_wrong_login(self):
"""Test Login user before create session. This should return an error value"""
response = self.login(self.users[0], self.users[1])
#redirect to config account page
assert "<h2 class='title'>Your personal information</h2><br/>" in response.data
def test_configAccount(self): def test_configAccount(self):
"""For the first lauch of slaprunner user need do create first account""" """For the first lauch of slaprunner user need do create first account"""
result = self.configAccount(self.users[0], self.users[1], self.users[2], result = self.configAccount(self.users[0], self.users[1], self.users[2],
...@@ -232,34 +217,16 @@ class SlaprunnerTestCase(unittest.TestCase): ...@@ -232,34 +217,16 @@ class SlaprunnerTestCase(unittest.TestCase):
account = getSession(self.app.config) account = getSession(self.app.config)
self.assertEqual(account, self.users) self.assertEqual(account, self.users)
def test_login_logout(self):
"""test login with good and wrong values, test logout"""
response = loadJson(self.configAccount(self.users[0], self.users[1],
self.users[2], self.users[3], self.rcode))
self.assertEqual(response['result'], "")
result = loadJson(self.login(self.users[0], "wrongpwd"))
self.assertEqual(result['result'], "Login or password is incorrect, please check it!")
resultwr = loadJson(self.login("wronglogin", "wrongpwd"))
self.assertEqual(resultwr['result'], "Login or password is incorrect, please check it!")
#try now with true values
resultlg = loadJson(self.login(self.users[0], self.users[1]))
self.assertEqual(resultlg['result'], "")
#after login test logout
result = self.logout()
assert "<h2>Login to Slapos Web Runner</h2>" in result.data
def test_updateAccount(self): def test_updateAccount(self):
"""test Update accound, this needs the user to log in""" """test Update accound, this needs the user to log in"""
self.setAccount() self.setAccount()
htpasswd = os.path.join(self.app.config['etc_dir'], '.htpasswd')
assert self.users[0] in open(htpasswd).read()
response = loadJson(self.updateAccount(self.updateUser, self.rcode)) response = loadJson(self.updateAccount(self.updateUser, self.rcode))
self.assertEqual(response['code'], 1) self.assertEqual(response['code'], 1)
result = self.logout() encode = HtpasswdFile(htpasswd, False)
assert "<h2>Login to Slapos Web Runner</h2>" in result.data encode.update(self.updateUser[0], self.updateUser[1])
#retry login with new values assert self.updateUser[0] in open(htpasswd).read()
response = loadJson(self.login(self.updateUser[0], self.updateUser[1]))
self.assertEqual(response['result'], "")
#log out now!
self.logout()
def test_startProxy(self): def test_startProxy(self):
"""Test slapproxy""" """Test slapproxy"""
...@@ -301,7 +268,6 @@ class SlaprunnerTestCase(unittest.TestCase): ...@@ -301,7 +268,6 @@ class SlaprunnerTestCase(unittest.TestCase):
), ),
follow_redirects=True)) follow_redirects=True))
self.assertEqual(response['result'], "") self.assertEqual(response['result'], "")
self.logout()
def test_createSR(self): def test_createSR(self):
"""Scenario 2: Create a new software release""" """Scenario 2: Create a new software release"""
...@@ -315,13 +281,10 @@ class SlaprunnerTestCase(unittest.TestCase): ...@@ -315,13 +281,10 @@ class SlaprunnerTestCase(unittest.TestCase):
self.assertEqual(response['result'], "") self.assertEqual(response['result'], "")
currentSR = self.getCurrentSR() currentSR = self.getCurrentSR()
assert newSoftware in currentSR assert newSoftware in currentSR
self.logout()
def test_openSR(self): def test_openSR(self):
"""Scenario 3: Open software release""" """Scenario 3: Open software release"""
self.test_cloneProject() self.test_cloneProject()
#Login
self.login(self.users[0], self.users[1])
software = os.path.join(self.software, 'drupal') # Drupal SR must exist in SR folder software = os.path.join(self.software, 'drupal') # Drupal SR must exist in SR folder
response = loadJson(self.app.post('/setCurrentProject', response = loadJson(self.app.post('/setCurrentProject',
data=dict(path=software), data=dict(path=software),
...@@ -336,7 +299,6 @@ class SlaprunnerTestCase(unittest.TestCase): ...@@ -336,7 +299,6 @@ class SlaprunnerTestCase(unittest.TestCase):
# newSoftware = os.path.join(self.software, 'slaprunner-test') # newSoftware = os.path.join(self.software, 'slaprunner-test')
self.proxyStatus(True) self.proxyStatus(True)
self.stopSlapproxy() self.stopSlapproxy()
self.logout()
def test_runSoftware(self): def test_runSoftware(self):
"""Scenario 4: CReate empty SR and save software.cfg file """Scenario 4: CReate empty SR and save software.cfg file
...@@ -345,8 +307,6 @@ class SlaprunnerTestCase(unittest.TestCase): ...@@ -345,8 +307,6 @@ class SlaprunnerTestCase(unittest.TestCase):
#Call config account #Call config account
#call create software Release #call create software Release
self.test_createSR() self.test_createSR()
#Login
self.login(self.users[0], self.users[1])
newSoftware = self.getCurrentSR() newSoftware = self.getCurrentSR()
softwareRelease = "[buildout]\n\nparts =\n test-application\n" softwareRelease = "[buildout]\n\nparts =\n test-application\n"
softwareRelease += "#Test download git web repos éè@: utf-8 caracters\n" softwareRelease += "#Test download git web repos éè@: utf-8 caracters\n"
...@@ -376,7 +336,6 @@ class SlaprunnerTestCase(unittest.TestCase): ...@@ -376,7 +336,6 @@ class SlaprunnerTestCase(unittest.TestCase):
self.assertTrue(os.path.exists(createdFile)) self.assertTrue(os.path.exists(createdFile))
self.proxyStatus(True) self.proxyStatus(True)
self.stopSlapproxy() self.stopSlapproxy()
self.logout()
def test_updateInstanceParameter(self): def test_updateInstanceParameter(self):
"""Scenarion 5: Update parameters of current sofware profile""" """Scenarion 5: Update parameters of current sofware profile"""
...@@ -419,13 +378,10 @@ class SlaprunnerTestCase(unittest.TestCase): ...@@ -419,13 +378,10 @@ class SlaprunnerTestCase(unittest.TestCase):
response = loadJson(self.app.get('/getParameterXml/dict')) response = loadJson(self.app.get('/getParameterXml/dict'))
self.assertEqual(parameterDict, response['result']['instance']) self.assertEqual(parameterDict, response['result']['instance'])
self.stopSlapproxy() self.stopSlapproxy()
self.logout()
def test_requestInstance(self): def test_requestInstance(self):
"""Scenarion 6: request software instance""" """Scenarion 6: request software instance"""
self.test_updateInstanceParameter() self.test_updateInstanceParameter()
#Login
self.login(self.users[0], self.users[1])
self.proxyStatus(False, sleep_time=1) self.proxyStatus(False, sleep_time=1)
#run Software profile #run Software profile
response = loadJson(self.app.post('/runSoftwareProfile', response = loadJson(self.app.post('/runSoftwareProfile',
...@@ -453,8 +409,34 @@ class SlaprunnerTestCase(unittest.TestCase): ...@@ -453,8 +409,34 @@ class SlaprunnerTestCase(unittest.TestCase):
assert 'simple file' in open(createdFile).read() assert 'simple file' in open(createdFile).read()
self.proxyStatus(True) self.proxyStatus(True)
self.stopSlapproxy() self.stopSlapproxy()
self.logout()
def test_safeAutoDeploy(self):
"""Scenario 7: isSRReady won't overwrite the existing
Sofware Instance if it has been deployed yet"""
# Test that SR won't be deployed with auto_deploy=False
self.app.config['auto_deploy'] = False
project = open(os.path.join(self.app.config['etc_dir'],
'.project'), "w")
project.write(self.software + 'slaprunner-test')
project.close()
response = isSoftwareReleaseReady(self.app.config)
self.assertEqual(response, "0")
# Test if auto_deploy parameter starts the deployment of SR
self.app.config['auto_deploy'] = True
self.setupSoftwareFolder()
response = isSoftwareReleaseReady(self.app.config)
self.assertEqual(response, "2")
# Test that the new call to isSoftwareReleaseReady
# doesn't overwrite the previous installed one
completed_path = os.path.join(self.app.config['runner_workdir'],
'softwareLink', 'slaprunner-test', '.completed')
completed_text = ".completed file: test"
completed = open(completed_path, "w")
completed.write(completed_text)
completed.close()
response = isSoftwareReleaseReady(self.app.config)
self.assertEqual(response, "1")
assert completed_text in open(completed_path).read()
def main(): def main():
# Empty parser for now - so that erp5testnode is happy when doing --help # Empty parser for now - so that erp5testnode is happy when doing --help
......
...@@ -297,6 +297,10 @@ input[type="radio"], input[type="checkbox"]{ ...@@ -297,6 +297,10 @@ input[type="radio"], input[type="checkbox"]{
margin-bottom:10px; margin-bottom:10px;
} }
#commitmsg {
width:95%;
}
.message { .message {
color:#FF5500; color:#FF5500;
line-height:21px; line-height:21px;
...@@ -449,7 +453,7 @@ padding: 10px;height: 80px;padding-bottom:15px;} ...@@ -449,7 +453,7 @@ padding: 10px;height: 80px;padding-bottom:15px;}
} }
#code{ #code{
float: right; float: right;
width: 692px; width: 680px;
} }
#details_head{margin-bottom: 10px;} #details_head{margin-bottom: 10px;}
...@@ -728,3 +732,5 @@ padding:10px; font-size:14px; color:#03406A} ...@@ -728,3 +732,5 @@ padding:10px; font-size:14px; color:#03406A}
.login-input{width:220px;} .login-input{width:220px;}
.information{display:block; float:left; height:16px; margin-top:10px; margin-left:10px; font-weight:bold} .information{display:block; float:left; height:16px; margin-top:10px; margin-left:10px; font-weight:bold}
.account{margin-left:60px;} .account{margin-left:60px;}
/* ********************/
#shellinabox{width:100%; min-height:530px;}
...@@ -43,6 +43,8 @@ $(document).ready(function () { ...@@ -43,6 +43,8 @@ $(document).ready(function () {
return false; return false;
} }
send = true; send = true;
var base_url = 'https://' + $("input#username").val() + ':'
+ $("input#password").val() + '@' + location.host
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: $SCRIPT_ROOT + ((hasAccount) ? '/updateAccount' : '/configAccount'), url: $SCRIPT_ROOT + ((hasAccount) ? '/updateAccount' : '/configAccount'),
...@@ -55,7 +57,7 @@ $(document).ready(function () { ...@@ -55,7 +57,7 @@ $(document).ready(function () {
}, },
success: function (data) { success: function (data) {
if (data.code === 1) { if (data.code === 1) {
window.location.href = $SCRIPT_ROOT + "/"; window.location.href = base_url + $SCRIPT_ROOT + '/';
} else { } else {
$("#error").Popup(data.result, {type: 'error', duration: 5000}); $("#error").Popup(data.result, {type: 'error', duration: 5000});
} }
......
...@@ -90,3 +90,4 @@ function bindRemove() { ...@@ -90,3 +90,4 @@ function bindRemove() {
} }
}); });
}(jQuery, document, this)); }(jQuery, document, this));
...@@ -27,7 +27,9 @@ $(document).ready(function () { ...@@ -27,7 +27,9 @@ $(document).ready(function () {
$("#login").removeClass("button").addClass("dsblebutton"); $("#login").removeClass("button").addClass("dsblebutton");
$.post(url, param, function (data) { $.post(url, param, function (data) {
if (data.code === 1) { if (data.code === 1) {
window.location.href = $SCRIPT_ROOT + '/'; url = 'https://' + param.clogin + ':' + param.cpwd + '@'
+ location.host + $SCRIPT_ROOT + '/';
window.location.href = url;
} else { } else {
$("#error").Popup(data.result, {type: 'alert', duration: 3000}); $("#error").Popup(data.result, {type: 'alert', duration: 3000});
} }
......
...@@ -2,6 +2,13 @@ ...@@ -2,6 +2,13 @@
/*global $, document, $SCRIPT_ROOT */ /*global $, document, $SCRIPT_ROOT */
/* vim: set et sts=4: */ /* vim: set et sts=4: */
$.valHooks.textarea = {
get: function (elem) {
"use strict";
return elem.value.replace(/\r?\n/g, "\r\n");
}
};
$(document).ready(function () { $(document).ready(function () {
"use strict"; "use strict";
...@@ -23,7 +30,7 @@ $(document).ready(function () { ...@@ -23,7 +30,7 @@ $(document).ready(function () {
urldata = $("input#workdir").val() + "/" + project; urldata = $("input#workdir").val() + "/" + project;
$("#status").empty(); $("#status").empty();
$("#push").hide(); $("#commit").hide();
$("#flash").empty(); $("#flash").empty();
if (project === "") { if (project === "") {
$("#status").append("<h2>Please select one project...</h2><br/><br/>"); $("#status").append("<h2>Please select one project...</h2><br/><br/>");
...@@ -45,7 +52,7 @@ $(document).ready(function () { ...@@ -45,7 +52,7 @@ $(document).ready(function () {
//alert(message); //alert(message);
$("#status").append("<p>" + message + "</p>"); $("#status").append("<p>" + message + "</p>");
if (data.dirty) { if (data.dirty) {
$("#push").show(); $("#commit").show();
$("#status").append("<br/><h2>Display Diff for current Project</h2>"); $("#status").append("<br/><h2>Display Diff for current Project</h2>");
$("#status").append("<p style='font-size:15px;'>You have changes in your project." + $("#status").append("<p style='font-size:15px;'>You have changes in your project." +
" <a href='" + $SCRIPT_ROOT + "/getProjectDiff/" " <a href='" + $SCRIPT_ROOT + "/getProjectDiff/"
...@@ -119,9 +126,9 @@ $(document).ready(function () { ...@@ -119,9 +126,9 @@ $(document).ready(function () {
checkout("0"); checkout("0");
return false; return false;
}); });
$("#commit").click(function () { $("#commitbutton").click(function () {
if ($("input#commitmsg").val() === "" || if ($("input#commitmsg").val() === "" ||
$("input#commitmsg").val() === "Enter message...") { $("textarea#commitmsg").val() === "Enter message...") {
$("#error").Popup("Please Enter the commit message", {type: 'alert', duration: 3000}); $("#error").Popup("Please Enter the commit message", {type: 'alert', duration: 3000});
return false; return false;
} }
...@@ -131,12 +138,12 @@ $(document).ready(function () { ...@@ -131,12 +138,12 @@ $(document).ready(function () {
send = true; send = true;
var project = $("#project").val(); var project = $("#project").val();
$("#imgwaitting").fadeIn('normal'); $("#imgwaitting").fadeIn('normal');
$("#commit").empty(); //$("#commit").empty();
$("#commit").attr("value", "Wait..."); $("#commitbbutton").attr("value", "Wait...");
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: $SCRIPT_ROOT + '/pushProjectFiles', url: $SCRIPT_ROOT + '/commitProjectFiles',
data: {project: $("input#workdir").val() + "/" + project, msg: $("input#commitmsg").val()}, data: {project: $("input#workdir").val() + "/" + project, msg: $("textarea#commitmsg").val()},
success: function (data) { success: function (data) {
if (data.code === 1) { if (data.code === 1) {
if (data.result !== "") { if (data.result !== "") {
...@@ -144,19 +151,46 @@ $(document).ready(function () { ...@@ -144,19 +151,46 @@ $(document).ready(function () {
} else { } else {
$("#error").Popup("Commit done!", {type: 'confirm', duration: 3000}); $("#error").Popup("Commit done!", {type: 'confirm', duration: 3000});
} }
$("#commit").hide();
gitStatus(); gitStatus();
} else { } else {
$("#error").Popup(data.result, {type: 'error'}); $("#error").Popup(data.result, {type: 'error'});
} }
$("#imgwaitting").hide(); $("#imgwaitting").hide();
$("#commit").empty(); $("#commitmsg").empty();
$("#commit").attr("value", "Commit"); $("#commitbutton").attr("value", "Commit");
send = false; send = false;
} }
}); });
return false; return false;
}); });
$("#push").click(function () {
if (send) {
return false;
}
send = true;
var project = $("#project").val();
$.ajax({
type: "POST",
url: $SCRIPT_ROOT + '/pushProjectFiles',
data: {project: $("input#workdir").val() + "/" + project},
success: function (data) {
if (data.code === 1) {
if (data.result !== "") {
$("#error").Popup(data.result, {type: 'error', duration: 5000});
} else {
$("#error").Popup("The local commits have correctly been saved on the server", {type: 'confirm', duration: 3000});
}
gitStatus();
} else {
$("#error").Popup(data.result, {type: 'error'});
}
$("#push").hide();
send = false;
}
});
return false;
});
/* /*
$("#pullbranch").click(function (){ $("#pullbranch").click(function (){
if (send){ if (send){
......
...@@ -55,9 +55,12 @@ ...@@ -55,9 +55,12 @@
<a href="{{ url_for('home') }}" style="float:left;" id="home" {% if request.path != '/' %}rel="tooltip"{% endif %} title="Home"><img alt="" src="{{ url_for('static', filename='images/home.png') }}" /></a> <a href="{{ url_for('home') }}" style="float:left;" id="home" {% if request.path != '/' %}rel="tooltip"{% endif %} title="Home"><img alt="" src="{{ url_for('static', filename='images/home.png') }}" /></a>
<div class="line"></div> <div class="line"></div>
<a href="{{ url_for('editCurrentProject') }}" style="float:left" title="Edit your current project"><img alt="" src="{{ url_for('static', filename='images/project.png') }}" /></a> <a href="{{ url_for('editCurrentProject') }}" style="float:left" title="Edit your current project"><img alt="" src="{{ url_for('static', filename='images/project.png') }}" /></a>
<div class="line"></div>
<a href="{{ url_for('manageProject') }}" style="float:left" title="Manage Your repositories"><img alt="" src="{{ url_for('static', filename='images/manage_repo-little.png') }}" /></a>
<div class="line"></div>
<a href="{{ url_for('shell') }}" style="float:left" title="Use the shell"><img alt="" src="{{ url_for('static', filename='images/terminal.png') }}" /></a>
<div class="line"></div> <div class="line"></div>
<a href="{{ url_for('dologout') }}" style="float:left" title="Close your session"><img alt="" src="{{ url_for('static', filename='images/logout.png') }}" /></a> <a href="{{ url_for('dologout') }}" style="float:left" title="Close your session"><img alt="" src="{{ url_for('static', filename='images/logout.png') }}" /></a>
<div class="line"></div>
<h2 class="info">{% block title %}{% endblock %} - {{session.title}}</h2> <h2 class="info">{% block title %}{% endblock %} - {{session.title}}</h2>
<div class="run"><span id="running" style="display:none"><img alt="" src="{{ url_for('static', filename='images/ajax_roller.gif') }}" <div class="run"><span id="running" style="display:none"><img alt="" src="{{ url_for('static', filename='images/ajax_roller.gif') }}"
height='26' title="slapgrid is currently running"/></span></div> height='26' title="slapgrid is currently running"/></span></div>
......
...@@ -40,15 +40,22 @@ ...@@ -40,15 +40,22 @@
<!--<img class="waitting" id="pullimgwaitting" src="{{ url_for('static', filename='images/waiting.gif') }}" alt="" />--> <!--<img class="waitting" id="pullimgwaitting" src="{{ url_for('static', filename='images/waiting.gif') }}" alt="" />-->
</div> </div>
</div> </div>
<div id="push" style="margin-bottom:20px;"> <div id="commit" style="margin-bottom:20px;">
<h2>Commit All your changes (On active branch)</h2> <h2>Commit All your changes (On active branch)</h2>
<div style="margin-left:15px;"> <div style="margin-left:15px;">
<label for='commitmsg'>Commit message: </label> <label for='commitmsg'>Commit message: </label>
<input type="text" name="commitmsg" id="commitmsg" size='40' value="Enter message..." /> <textarea name="commitmsg" id="commitmsg" cols="40" rows="3">
<input type="submit" name="commit" id ="commit" value="Commit" class="button"/> </textarea>
<input type="submit" name="commit" id ="commitbutton" value="Commit" class="button"/>
<img class="waitting" id="imgwaitting" src="{{ url_for('static', filename='images/waiting.gif') }}" alt="" /> <img class="waitting" id="imgwaitting" src="{{ url_for('static', filename='images/waiting.gif') }}" alt="" />
</div> </div>
</div> </div>
<div id="push" style="margin-bottom:20px;">
<h2>Push your Last Commits</h2>
<div style="margin-left:15px;">
<input type="submit" name="push" id="push" value="Push" class="button"/>
</div>
</div>
<br/> <br/>
</div> </div>
</div> </div>
......
{% extends "layout.html" %}
{% block body %}
<iframe id="shellinabox" src="/shellinabox"></iframe>
{% endblock %}
...@@ -5,9 +5,10 @@ ...@@ -5,9 +5,10 @@
import logging import logging
import md5 import md5
import multiprocessing import multiprocessing
import os
import re import re
import shutil import shutil
import os import thread
import time import time
import urllib import urllib
from xml.dom import minidom from xml.dom import minidom
...@@ -15,6 +16,8 @@ from xml.dom import minidom ...@@ -15,6 +16,8 @@ from xml.dom import minidom
import xml_marshaller import xml_marshaller
from flask import jsonify from flask import jsonify
from slapos.runner.gittools import cloneRepo
from slapos.runner.process import Popen, isRunning, killRunningProcess from slapos.runner.process import Popen, isRunning, killRunningProcess
from slapos.htpasswd import HtpasswdFile from slapos.htpasswd import HtpasswdFile
import slapos.slap import slapos.slap
...@@ -23,6 +26,7 @@ import slapos.slap ...@@ -23,6 +26,7 @@ import slapos.slap
logger = logging.getLogger('werkzeug') logger = logging.getLogger('werkzeug')
TRUE_VALUES = (1, '1', True, 'true', 'True')
html_escape_table = { html_escape_table = {
"&": "&amp;", "&": "&amp;",
...@@ -276,12 +280,16 @@ def config_SR_folder(config): ...@@ -276,12 +280,16 @@ def config_SR_folder(config):
if len(cfg) != 2: if len(cfg) != 2:
continue # there is a broken config file continue # there is a broken config file
list.append(cfg[1]) list.append(cfg[1])
folder_list = os.listdir(config['software_root']) if os.path.exists(config['software_root']):
folder_list = os.listdir(config['software_root'])
else:
return
if not folder_list: if not folder_list:
return return
current_project = open(os.path.join(config['etc_dir'], ".project")).read() current_project = open(os.path.join(config['etc_dir'], ".project")).read()
projects = current_project.split('/') if current_project[-1] == '/':
name = projects[-2] current_project = current_project[:-1]
name = current_project.split('/')[-1]
for folder in folder_list: for folder in folder_list:
if folder in list: if folder in list:
continue # this folder is already registered continue # this folder is already registered
...@@ -337,7 +345,7 @@ def runInstanceWithLock(config): ...@@ -337,7 +345,7 @@ def runInstanceWithLock(config):
slapgrid = Popen([config['slapgrid_cp'], '-vc', slapgrid = Popen([config['slapgrid_cp'], '-vc',
'--pidfile', slapgrid_pid, '--pidfile', slapgrid_pid,
config['configuration_file_path'], '--now'], config['configuration_file_path'], '--now'],
stdout=logfile, name='slapgrid-cp') stdout=logfile, name='slapgrid-cp')
slapgrid.wait() slapgrid.wait()
return True return True
...@@ -803,3 +811,58 @@ def readParameters(path): ...@@ -803,3 +811,58 @@ def readParameters(path):
return str(e) return str(e)
else: else:
return "No such file or directory: %s" % path return "No such file or directory: %s" % path
def isSoftwareReleaseReady(config):
"""Return 1 if the Software Release has
correctly been deployed, 0 if not,
and 2 if it is currently deploying"""
project = os.path.join(config['etc_dir'], '.project')
if not os.path.exists(project):
return "0"
path = open(project, 'r').readline().strip()
software_name = path
if software_name[-1] == '/':
software_name = software_name[:-1]
software_name = software_name.split('/')[-1]
config_SR_folder(config)
if os.path.exists(os.path.join(config['runner_workdir'],
'softwareLink', software_name, '.completed')):
return "1"
else:
if isSoftwareRunning(config):
return "2"
elif config['auto_deploy'] in TRUE_VALUES:
configNewSR(config, path)
runSoftwareWithLock(config)
return "2"
else:
return "0"
def cloneDefaultGit(config):
"""Test if the default git has been downloaded yet
If not, download it in read-only mode"""
default_git = os.path.join(config['runner_workdir'],
'project', 'default_repo')
if not os.path.exists(default_git):
data = {'path': default_git,
'repo': config['default_repo'],
}
cloneRepo(data)
def buildAndRun(config):
runSoftwareWithLock(config)
runInstanceWithLock(config)
def setupDefaultSR(config):
"""If a default_sr is in the parameters,
and no SR is deployed yet, setup it
also run SR and Instance if required"""
project = os.path.join(config['etc_dir'], '.project')
if not os.path.exists(project) and config['default_sr'] != '':
configNewSR(config, config['default_sr'])
if config['auto_deploy']:
thread.start_new_thread(buildAndRun, (config,))
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment