Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
slapos
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Léo-Paul Géneau
slapos
Commits
28ace582
Commit
28ace582
authored
Jul 01, 2021
by
Xavier Thompson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
software/theia: Add resiliency tests
parent
95afd1c9
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
607 additions
and
0 deletions
+607
-0
software/theia/test/resilience_dummy/README.md
software/theia/test/resilience_dummy/README.md
+6
-0
software/theia/test/resilience_dummy/instance.cfg.in
software/theia/test/resilience_dummy/instance.cfg.in
+47
-0
software/theia/test/resilience_dummy/software.cfg
software/theia/test/resilience_dummy/software.cfg
+28
-0
software/theia/test/test_resiliency.py
software/theia/test/test_resiliency.py
+526
-0
No files found.
software/theia/test/resilience_dummy/README.md
0 → 100644
View file @
28ace582
# Resilience Dummy Software
A very simple SR to test resiliency:
-
fast installation and deployment
-
self-contained - no dependency outside this folder
-
has a simple
`exporter.exclude`
and
`runner-import-restore`
software/theia/test/resilience_dummy/instance.cfg.in
0 → 100644
View file @
28ace582
[buildout]
parts =
log-writer
exporter.exclude
runner-import-restore
backup-identity-script
eggs-directory = ${buildout:eggs-directory}
develop-eggs-directory = ${buildout:develop-eggs-directory}
offline = true
[directory]
recipe = plone.recipe.command
home = $${buildout:directory}
srv = $${:home}/srv
etc = $${:home}/etc
run = $${:etc}/run
command = mkdir -p $${:run} $${:srv}
[log-writer]
recipe = slapos.recipe.template:jinja2
template = inline:#!/bin/sh
echo "Hello : $(date)" >> $${directory:home}/log.log
rendered = $${directory:run}/log-writer
[exporter.exclude]
recipe = slapos.recipe.template:jinja2
template = inline:$${directory:home}/exclude
rendered = $${directory:srv}/exporter.exclude
[runner-import-restore]
recipe = slapos.recipe.template:jinja2
template = inline:#!/bin/sh
echo "Hello : $(date)" >> $${directory:home}/runner-import-restore.log
exit $TEST_RESTORE_STATUS
rendered = $${directory:srv}/runner-import-restore
[backup-identity-script]
recipe = slapos.recipe.template:jinja2
template = inline:#!/bin/sh
echo "Custom script"
for i in "$@"
do
echo $(sha256sum $i)
done
exit $TEST_BACKUP_STATUS
rendered = $${directory:srv}/.backup_identity_script
software/theia/test/resilience_dummy/software.cfg
0 → 100644
View file @
28ace582
[buildout]
find-links +=
http://www.nexedi.org/static/packages/source/
http://www.nexedi.org/static/packages/source/slapos.buildout/
parts =
instance-template
plone-recipe-command
versions = versions
[plone-recipe-command]
recipe = zc.recipe.egg
eggs = plone.recipe.command
[instance-template]
recipe = slapos.recipe.template
url = ${:_profile_base_location_}/instance.cfg.in
output = ${buildout:directory}/instance.cfg
mode = 0644
[versions]
setuptools = 44.0.0
zc.buildout = 2.7.1+slapos010
zc.recipe.egg = 2.0.3+slapos003
Jinja2 = 2.11.2
MarkupSafe = 1.0
software/theia/test/test_resiliency.py
0 → 100644
View file @
28ace582
##############################################################################
#
# Copyright (c) 2019 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from
__future__
import
unicode_literals
import
errno
import
os
import
re
import
shutil
import
subprocess
import
time
import
requests
from
slapos.testing.testcase
import
SlapOSNodeCommandError
,
installSoftwareUrlList
from
test
import
TheiaTestCase
,
ResilientTheiaMixin
,
theia_software_release_url
dummy_software_url
=
os
.
path
.
abspath
(
os
.
path
.
join
(
'resilience_dummy'
,
'software.cfg'
))
class
WorkaroundSnapshotConflict
(
TheiaTestCase
):
@
classmethod
def
_copySnapshot
(
cls
,
source_file_name
,
name
):
# Workaround setUpModule snapshots name conflicts
if
not
name
.
startswith
(
cls
.
__module__
):
name
=
'%s.%s'
%
(
cls
.
__module__
,
name
)
super
(
WorkaroundSnapshotConflict
,
cls
).
_copySnapshot
(
source_file_name
,
name
)
def
setUpModule
():
installSoftwareUrlList
(
WorkaroundSnapshotConflict
,
[
theia_software_release_url
],
debug
=
bool
(
int
(
os
.
environ
.
get
(
'SLAPOS_TEST_DEBUG'
,
0
))),
)
class
ResilientTheiaTestCase
(
ResilientTheiaMixin
,
TheiaTestCase
):
@
classmethod
def
_processEmbeddedInstance
(
cls
,
retries
=
0
,
software_type
=
'export'
):
slapos
=
cls
.
_getSlapos
(
software_type
)
for
_
in
range
(
retries
):
try
:
output
=
subprocess
.
check_output
((
slapos
,
'node'
,
'instance'
),
stderr
=
subprocess
.
STDOUT
)
except
subprocess
.
CalledProcessError
:
continue
print
(
output
)
break
else
:
if
retries
:
# Sleep a bit as an attempt to workaround monitoring boostrap not being ready
print
(
"Wait before running slapos node instance one last time"
)
time
.
sleep
(
120
)
subprocess
.
check_call
((
slapos
,
'node'
,
'instance'
))
@
classmethod
def
_deployEmbeddedSoftware
(
cls
,
software_url
,
instance_name
,
retries
=
0
,
software_type
=
'export'
):
slapos
=
cls
.
_getSlapos
(
software_type
)
subprocess
.
check_call
((
slapos
,
'supply'
,
software_url
,
'slaprunner'
))
try
:
subprocess
.
check_output
((
slapos
,
'node'
,
'software'
),
stderr
=
subprocess
.
STDOUT
)
except
subprocess
.
CalledProcessError
as
e
:
print
(
e
.
output
)
raise
subprocess
.
check_call
((
slapos
,
'request'
,
instance_name
,
software_url
))
cls
.
_processEmbeddedInstance
(
retries
,
software_type
)
@
classmethod
def
getInstanceParameterDict
(
cls
):
return
{
'autorun'
:
'stopped'
}
class
ResilienceMixin
(
object
):
def
_prepareExport
(
self
):
pass
def
_doSync
(
self
):
raise
NotImplementedError
def
_checkSync
(
self
):
pass
def
_doTakeover
(
self
):
raise
NotImplementedError
def
_checkTakeover
(
self
):
pass
def
test
(
self
):
# Do stuff on the main instance
# e.g. deploy an embedded software instance
self
.
_prepareExport
()
# Backup the main instance to a clone
# i.e. call export and import scripts
self
.
_doSync
()
# Check that the export-backup-import process went well
# e.g. look at logs and compare data
self
.
_checkSync
()
# Let the clone become a main instance
# i.e. start embedded services
self
.
_doTakeover
()
# Check that the takeover went well
# e.g. check services
self
.
_checkTakeover
()
class
ExportAndImportMixin
(
object
):
def
getExportExitfile
(
self
):
return
self
.
_getPartitionPath
(
'export'
,
'srv'
,
'export-exitcode-file'
)
def
getExportErrorfile
(
self
):
return
self
.
_getPartitionPath
(
'export'
,
'srv'
,
'export-errormessage-file'
)
def
getImportExitfile
(
self
):
return
self
.
_getPartitionPath
(
'import'
,
'srv'
,
'import-exitcode-file'
)
def
getImportErrorfile
(
self
):
return
self
.
_getPartitionPath
(
'import'
,
'srv'
,
'import-errormessage-file'
)
def
makedirs
(
self
,
path
):
try
:
os
.
makedirs
(
path
if
os
.
path
.
isdir
(
path
)
else
os
.
path
.
dirname
(
path
))
except
OSError
as
e
:
if
e
.
errno
!=
errno
.
EEXIST
:
raise
def
writeFile
(
self
,
path
,
content
,
mode
=
'w'
):
self
.
makedirs
(
path
)
executable
=
mode
==
'exec'
mode
=
'w'
if
executable
else
mode
with
open
(
path
,
mode
)
as
f
:
if
executable
:
f
.
write
(
'#!/bin/sh
\
n
'
)
f
.
write
(
content
)
if
executable
:
os
.
chmod
(
path
,
0o700
)
def
assertPromiseSucess
(
self
):
# Force promises to recompute regardless of periodicity
self
.
slap
.
_force_slapos_node_instance_all
=
True
try
:
self
.
slap
.
waitForInstance
(
error_lines
=
0
)
except
SlapOSNodeCommandError
as
e
:
s
=
str
(
e
)
self
.
assertNotIn
(
"Promise 'resiliency-export-promise.py' failed"
,
s
)
self
.
assertNotIn
(
'ERROR export script'
,
s
)
self
.
assertNotIn
(
"Promise 'resiliency-import-promise.py' failed"
,
s
)
self
.
assertNotIn
(
'ERROR import script'
,
s
)
else
:
pass
def
_doExport
(
self
):
# Compute last modification of the export exitcode file
exitfile
=
self
.
getExportExitfile
()
initial_exitdate
=
os
.
path
.
getmtime
(
exitfile
)
# Call export script manually
theia_export_script
=
self
.
_getPartitionPath
(
'export'
,
'bin'
,
'theia-export-script'
)
subprocess
.
check_call
((
theia_export_script
,),
stderr
=
subprocess
.
STDOUT
)
# Check that the export exitcode file was modified
self
.
assertGreater
(
os
.
path
.
getmtime
(
exitfile
),
initial_exitdate
)
with
open
(
exitfile
)
as
f
:
self
.
assertEqual
(
'0'
,
f
.
read
())
# Check promises
self
.
assertPromiseSucess
()
def
_doTransfer
(
self
):
# Copy <export>/srv/backup/theia to <import>/srv/backup/theia manually
export_backup_path
=
self
.
_getPartitionPath
(
'export'
,
'srv'
,
'backup'
,
'theia'
)
import_backup_path
=
self
.
_getPartitionPath
(
'import'
,
'srv'
,
'backup'
,
'theia'
)
shutil
.
rmtree
(
import_backup_path
)
shutil
.
copytree
(
export_backup_path
,
import_backup_path
)
def
_doImport
(
self
):
# Compute last modification of the import exitcode file
exitfile
=
self
.
getImportExitfile
()
initial_exitdate
=
os
.
path
.
getmtime
(
exitfile
)
# Call the import script manually
theia_import_script
=
self
.
_getPartitionPath
(
'import'
,
'bin'
,
'theia-import-script'
)
subprocess
.
check_call
((
theia_import_script
,),
stderr
=
subprocess
.
STDOUT
)
# Check that the import exitcode file was updated
self
.
assertGreater
(
os
.
path
.
getmtime
(
exitfile
),
initial_exitdate
)
with
open
(
exitfile
)
as
f
:
self
.
assertEqual
(
'0'
,
f
.
read
())
# Check promises
self
.
assertPromiseSucess
()
class
TestTheiaExportAndImportFailures
(
ExportAndImportMixin
,
ResilientTheiaTestCase
):
script_relpath
=
os
.
path
.
join
(
'srv'
,
'runner'
,
'instance'
,
'slappart0'
,
'srv'
,
'.backup_identity_script'
)
def
assertPromiseFailure
(
self
,
*
msg
):
# Force promises to recompute regardless of periodicity
self
.
slap
.
_force_slapos_node_instance_all
=
True
try
:
self
.
slap
.
waitForInstance
(
error_lines
=
0
)
except
SlapOSNodeCommandError
as
e
:
s
=
str
(
e
).
replace
(
'
\
\
n'
,
'
\
n
'
)
for
m
in
msg
:
self
.
assertIn
(
m
,
s
)
else
:
self
.
fail
(
'No promise failed'
)
def
assertScriptFailure
(
self
,
func
,
errorfile
,
exitfile
,
*
msg
):
self
.
assertRaises
(
subprocess
.
CalledProcessError
,
func
,
)
if
msg
:
with
open
(
errorfile
)
as
f
:
error
=
f
.
read
()
for
m
in
msg
:
self
.
assertIn
(
m
,
error
)
with
open
(
exitfile
)
as
f
:
self
.
assertNotEqual
(
'0'
,
f
.
read
())
def
assertExportFailure
(
self
,
*
msg
):
self
.
assertScriptFailure
(
self
.
_doExport
,
self
.
getExportErrorfile
(),
self
.
getExportExitfile
(),
*
msg
)
self
.
assertPromiseFailure
(
'ERROR export script failed'
,
*
msg
)
def
assertImportFailure
(
self
,
*
msg
):
self
.
assertScriptFailure
(
self
.
_doImport
,
self
.
getImportErrorfile
(),
self
.
getImportExitfile
(),
*
msg
)
self
.
assertPromiseFailure
(
'ERROR import script failed'
,
*
msg
)
def
customScript
(
self
,
path
,
content
=
None
):
if
content
:
self
.
writeFile
(
path
,
content
,
mode
=
'exec'
)
else
:
if
os
.
path
.
exists
(
path
):
os
.
remove
(
path
)
def
customSignatureScript
(
self
,
content
=
None
):
custom_script
=
self
.
_getPartitionPath
(
'export'
,
self
.
script_relpath
)
self
.
customScript
(
custom_script
,
content
)
def
customRestoreScript
(
self
,
content
=
None
):
restore_script
=
self
.
_getPartitionPath
(
'import'
,
'srv'
,
'runner-import-restore'
)
self
.
customScript
(
restore_script
,
content
)
return
restore_script
def
cleanupExitfiles
(
self
):
self
.
writeFile
(
self
.
getExportExitfile
(),
'0'
)
self
.
writeFile
(
self
.
getImportExitfile
(),
'0'
)
def
setUp
(
self
):
self
.
customSignatureScript
(
content
=
None
)
self
.
customRestoreScript
(
content
=
None
)
self
.
cleanupExitfiles
()
def
test_export_promise
(
self
):
self
.
writeFile
(
self
.
getExportExitfile
(),
'1'
)
self
.
assertPromiseFailure
(
'ERROR export script failed'
)
def
test_import_promise
(
self
):
self
.
writeFile
(
self
.
getImportExitfile
(),
'1'
)
self
.
assertPromiseFailure
(
'ERROR import script failed'
)
def
test_custom_hash_script
(
self
):
errmsg
=
'Bye bye'
self
.
customSignatureScript
(
content
=
'>&2 echo "%s"
\
n
exit 1'
%
errmsg
)
backup_script
=
self
.
_getPartitionPath
(
'export'
,
'srv'
,
'backup'
,
'theia'
,
self
.
script_relpath
)
self
.
assertExportFailure
(
'Compute backup signature
\
n
... ERROR !'
,
'Custom signature script %s failed'
%
os
.
path
.
abspath
(
backup_script
),
'and stderr:
\
n
%s'
%
errmsg
)
def
test_signature_mismatch
(
self
):
signature_file
=
self
.
_getPartitionPath
(
'import'
,
'srv'
,
'backup'
,
'theia'
,
'backup.signature'
)
moved_file
=
self
.
_getPartitionPath
(
'import'
,
'srv'
,
'backup'
,
'backup.signature.moved'
)
self
.
writeFile
(
moved_file
,
'Bogus Hash
\
n
'
,
mode
=
'a'
)
os
.
rename
(
moved_file
,
signature_file
)
self
.
assertImportFailure
(
'ERROR the backup signatures do not match'
)
def
test_restore_script_error
(
self
):
self
.
_doExport
()
self
.
_doTransfer
()
restore_script
=
self
.
customRestoreScript
(
'exit 1'
)
self
.
assertImportFailure
(
'Run custom restore script %s
\
n
... ERROR !'
%
restore_script
)
class
TestTheiaExportAndImport
(
ResilienceMixin
,
ExportAndImportMixin
,
ResilientTheiaTestCase
):
def
checkLog
(
self
,
log_path
,
initial
=
[],
newline
=
"Hello"
):
with
open
(
log_path
)
as
f
:
log
=
f
.
readlines
()
self
.
assertEqual
(
len
(
log
),
len
(
initial
)
+
int
(
bool
(
newline
)))
for
line
,
initial_line
in
zip
(
log
,
initial
):
self
.
assertEqual
(
line
,
initial_line
)
if
newline
:
self
.
assertTrue
(
log
[
-
1
].
startswith
(
newline
),
log
[
-
1
])
return
log
def
_prepareExport
(
self
):
# Copy ./resilience_dummy SR in export theia ~/srv/project/dummy
dummy_target_path
=
self
.
_getPartitionPath
(
'export'
,
'srv'
,
'project'
,
'dummy'
)
shutil
.
copytree
(
os
.
path
.
dirname
(
dummy_software_url
),
dummy_target_path
)
self
.
_test_software_url
=
os
.
path
.
join
(
dummy_target_path
,
'software.cfg'
)
# Deploy dummy instance in export partition
self
.
_deployEmbeddedSoftware
(
self
.
_test_software_url
,
'dummy_instance'
)
relpath_dummy
=
os
.
path
.
join
(
'srv'
,
'runner'
,
'instance'
,
'slappart0'
)
self
.
export_dummy_root
=
dummy_root
=
self
.
_getPartitionPath
(
'export'
,
relpath_dummy
)
self
.
import_dummy_root
=
self
.
_getPartitionPath
(
'import'
,
relpath_dummy
)
# Check that dummy instance was properly deployed
self
.
initial_log
=
self
.
checkLog
(
os
.
path
.
join
(
dummy_root
,
'log.log'
))
# Create ~/include and ~/include/included
self
.
writeFile
(
os
.
path
.
join
(
dummy_root
,
'include'
,
'included'
),
'This file should be included in resilient backup'
)
# Create ~/exclude and ~/exclude/excluded
self
.
writeFile
(
os
.
path
.
join
(
dummy_root
,
'exclude'
,
'excluded'
),
'This file should be excluded from resilient backup'
)
# Check that ~/srv/exporter.exclude and ~/srv/runner-import-restore
# As well as ~/srv/.backup_identity_script
self
.
assertTrue
(
os
.
path
.
exists
(
os
.
path
.
join
(
dummy_root
,
'srv'
,
'exporter.exclude'
)))
self
.
assertTrue
(
os
.
path
.
exists
(
os
.
path
.
join
(
dummy_root
,
'srv'
,
'runner-import-restore'
)))
self
.
assertTrue
(
os
.
path
.
exists
(
os
.
path
.
join
(
dummy_root
,
'srv'
,
'.backup_identity_script'
)))
def
_doSync
(
self
):
self
.
_doExport
()
self
.
_doTransfer
()
self
.
_doImport
()
def
_checkSync
(
self
):
dummy_root
=
self
.
import_dummy_root
# Check that the software url is correct
adapted_test_url
=
self
.
_getPartitionPath
(
'import'
,
'srv'
,
'project'
,
'dummy'
,
'software.cfg'
)
proxy_content
=
subprocess
.
check_output
(
(
self
.
_getSlapos
(
'import'
),
'proxy'
,
'show'
),
universal_newlines
=
True
)
self
.
assertIn
(
adapted_test_url
,
proxy_content
)
self
.
assertNotIn
(
self
.
_test_software_url
,
proxy_content
)
# Check that ~/srv/project was exported
self
.
assertTrue
(
os
.
path
.
exists
(
adapted_test_url
))
# Check that the dummy instance is not yet started
self
.
checkLog
(
os
.
path
.
join
(
dummy_root
,
'log.log'
),
self
.
initial_log
,
newline
=
None
)
# Check that ~/srv/.backup_identity_script was called
signature
=
self
.
_getPartitionPath
(
'import'
,
'srv'
,
'backup'
,
'backup.signature.proof'
)
with
open
(
signature
)
as
f
:
self
.
assertIn
(
'Custom script'
,
f
.
read
())
# Check that ~/include and ~/include/included were included
self
.
assertTrue
(
os
.
path
.
exists
(
os
.
path
.
join
(
dummy_root
,
'include'
,
'included'
)))
# Check that ~/exclude was excluded
self
.
assertFalse
(
os
.
path
.
exists
(
os
.
path
.
join
(
dummy_root
,
'exclude'
)))
# Check that ~/srv/runner-import-restore was called
self
.
checkLog
(
os
.
path
.
join
(
dummy_root
,
'runner-import-restore.log'
))
def
_doTakeover
(
self
):
# Start the dummy instance as a sort of fake takeover
subprocess
.
check_call
((
self
.
_getSlapos
(
'import'
),
'node'
,
'instance'
))
def
_checkTakeover
(
self
):
# Check that dummy instance was properly re-deployed
log_path
=
os
.
path
.
join
(
self
.
import_dummy_root
,
'log.log'
)
self
.
checkLog
(
log_path
,
self
.
initial_log
)
class
TakeoverMixin
(
ExportAndImportMixin
):
def
_getTakeoverUrlAndPassword
(
self
,
scope
=
"theia-1"
):
parameter_dict
=
self
.
computer_partition
.
getConnectionParameterDict
()
takeover_url
=
parameter_dict
[
"takeover-%s-url"
%
scope
]
takeover_password
=
parameter_dict
[
"takeover-%s-password"
%
scope
]
return
takeover_url
,
takeover_password
def
_getTakeoverPage
(
self
,
takeover_url
):
resp
=
requests
.
get
(
takeover_url
,
verify
=
True
)
self
.
assertEqual
(
requests
.
codes
.
ok
,
resp
.
status_code
)
return
resp
.
text
def
_waitScriptDone
(
self
,
name
,
start
,
exitfile
,
errorfile
,
maxtries
,
interval
):
print
(
'Wait until %s script has run'
%
name
.
lower
())
for
t
in
range
(
maxtries
):
if
os
.
path
.
getmtime
(
exitfile
)
<
start
:
time
.
sleep
(
interval
)
continue
with
open
(
exitfile
)
as
f
:
if
f
.
read
()
==
'0'
:
print
(
name
+
' script ran successfully'
)
return
maxtries
-
t
print
(
name
+
' script failed:
\
n
'
)
with
open
(
errorfile
)
as
f
:
print
(
f
.
read
())
self
.
fail
(
name
+
' script failed'
)
self
.
fail
(
name
+
' script did not finish before timeout'
)
def
_waitTakeoverReady
(
self
,
takeover_url
,
start
,
maxtries
,
interval
):
export_exitfile
=
self
.
getExportExitfile
()
export_errorfile
=
self
.
getExportErrorfile
()
tries
=
self
.
_waitScriptDone
(
'Export'
,
start
,
export_exitfile
,
export_errorfile
,
maxtries
,
interval
)
import_exitfile
=
self
.
getImportExitfile
()
import_errorfile
=
self
.
getImportErrorfile
()
tries
=
self
.
_waitScriptDone
(
'Import'
,
start
,
import_exitfile
,
import_errorfile
,
tries
,
interval
)
for
_
in
range
(
tries
):
info
=
self
.
_getTakeoverPage
(
takeover_url
)
if
"No backup downloaded yet, takeover should not happen now."
in
info
:
print
(
'Takeover page still reports export script in progress'
)
elif
"<b>Importer script(s) of backup in progress:</b> True"
in
info
:
print
(
'Takeover page still reports import script in progress'
)
else
:
return
time
.
sleep
(
interval
)
self
.
fail
(
'Takeover page failed to report readiness'
)
def
_requestTakeover
(
self
,
takeover_url
,
takeover_password
):
resp
=
requests
.
get
(
"%s?password=%s"
%
(
takeover_url
,
takeover_password
),
verify
=
True
)
self
.
assertEqual
(
requests
.
codes
.
ok
,
resp
.
status_code
)
self
.
assertNotIn
(
"Error"
,
resp
.
text
,
"An Error occured: %s"
%
resp
.
text
)
self
.
assertIn
(
"Success"
,
resp
.
text
,
"An Error occured: %s"
%
resp
.
text
)
return
resp
.
text
class
TestTheiaResilience
(
ResilienceMixin
,
TakeoverMixin
,
ResilientTheiaTestCase
):
test_instance_max_retries
=
0
backup_max_tries
=
70
backup_wait_interval
=
10
_test_software_url
=
dummy_software_url
def
_prepareExport
(
self
):
# Deploy test instance
self
.
_deployEmbeddedSoftware
(
self
.
_test_software_url
,
'test_instance'
,
self
.
test_instance_max_retries
)
# Check that there is an export and import instance and get their partition IDs
self
.
export_id
=
self
.
_getPartitionId
(
'export'
)
self
.
import_id
=
self
.
_getPartitionId
(
'import'
)
def
_doSync
(
self
):
start
=
time
.
time
()
# Call exporter script instead of waiting for cron job
# XXX Accelerate cron frequency instead ?
exporter_script
=
self
.
_getPartitionPath
(
'export'
,
'bin'
,
'exporter'
)
transaction_id
=
str
(
int
(
time
.
time
()))
subprocess
.
check_call
((
exporter_script
,
'--transaction-id'
,
transaction_id
))
takeover_url
,
_
=
self
.
_getTakeoverUrlAndPassword
()
# Wait for takoever to be ready
self
.
_waitTakeoverReady
(
takeover_url
,
start
,
self
.
backup_max_tries
,
self
.
backup_wait_interval
)
def
_doTakeover
(
self
):
# Takeover
takeover_url
,
takeover_password
=
self
.
_getTakeoverUrlAndPassword
()
self
.
_requestTakeover
(
takeover_url
,
takeover_password
)
# Wait for import instance to become export instance and new import to be allocated
# This also checks that all promises of theia instances succeed
self
.
slap
.
waitForInstance
(
self
.
instance_max_retry
)
self
.
computer_partition
=
self
.
requestDefaultInstance
()
def
_checkTakeover
(
self
):
# Check that there is an export, import and frozen instance and get their new partition IDs
import_id
=
self
.
import_id
export_id
=
self
.
export_id
new_export_id
=
self
.
_getPartitionId
(
'export'
)
new_import_id
=
self
.
_getPartitionId
(
'import'
)
new_frozen_id
=
self
.
_getPartitionId
(
'frozen'
)
# Check that old export instance is now frozen
self
.
assertEqual
(
export_id
,
new_frozen_id
)
# Check that old import instance is now the new export instance
self
.
assertEqual
(
import_id
,
new_export_id
)
# Check that there is a new import instance
self
.
assertNotIn
(
new_import_id
,
(
export_id
,
new_export_id
))
# Check that the test instance is properly redeployed
# This checks the promises of the test instance
self
.
_processEmbeddedInstance
(
self
.
test_instance_max_retries
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment