2017-05-01 02:26:56 +02:00
#!/usr/bin/env python3
2020-11-09 13:23:58 +09:00
# SPDX-License-Identifier: LGPL-2.1-or-later
2022-09-02 20:14:53 +02:00
# pylint: disable=line-too-long,too-many-lines,too-many-branches,too-many-statements,too-many-arguments
# pylint: disable=too-many-public-methods,too-many-boolean-expressions,invalid-name,no-self-use
# pylint: disable=missing-function-docstring,missing-class-docstring,missing-module-docstring
2017-02-15 12:40:52 +01:00
#
2018-06-12 19:00:24 +02:00
# Copyright © 2017 Michal Sekletar <msekleta@redhat.com>
2017-02-15 12:40:52 +01:00
# ATTENTION: This uses the *installed* systemd, not the one from the built
# source tree.
import os
import subprocess
2020-02-03 13:07:45 +01:00
import sys
2022-09-02 20:14:53 +02:00
import time
import unittest
2022-09-13 20:14:48 +02:00
import uuid
2017-02-15 12:40:52 +01:00
from enum import Enum
2022-09-02 20:14:53 +02:00
2017-02-15 12:40:52 +01:00
class UnitFileChange ( Enum ) :
NO_CHANGE = 0
LINES_SWAPPED = 1
COMMAND_ADDED_BEFORE = 2
COMMAND_ADDED_AFTER = 3
COMMAND_INTERLEAVED = 4
REMOVAL = 5
class ExecutionResumeTest ( unittest . TestCase ) :
def setUp ( self ) :
self . unit = ' test-issue-518.service '
2022-09-02 20:14:53 +02:00
self . unitfile_path = f ' /run/systemd/system/ { self . unit } '
2022-09-13 20:14:48 +02:00
self . output_file = f " /tmp/test-issue-518- { uuid . uuid4 ( ) } "
2017-02-15 12:40:52 +01:00
self . unit_files = { }
2022-09-02 20:14:53 +02:00
unit_file_content = f '''
2017-02-15 12:40:52 +01:00
[ Service ]
Type = oneshot
2020-02-03 13:07:45 +01:00
ExecStart = / bin / sleep 3
2022-09-02 20:14:53 +02:00
ExecStart = / bin / bash - c " echo foo >> {self.output_file} "
'''
2017-02-15 12:40:52 +01:00
self . unit_files [ UnitFileChange . NO_CHANGE ] = unit_file_content
2022-09-02 20:14:53 +02:00
unit_file_content = f '''
2017-02-15 12:40:52 +01:00
[ Service ]
Type = oneshot
2022-09-02 20:14:53 +02:00
ExecStart = / bin / bash - c " echo foo >> {self.output_file} "
2020-02-03 13:07:45 +01:00
ExecStart = / bin / sleep 3
2022-09-02 20:14:53 +02:00
'''
2017-02-15 12:40:52 +01:00
self . unit_files [ UnitFileChange . LINES_SWAPPED ] = unit_file_content
2022-09-02 20:14:53 +02:00
unit_file_content = f '''
2017-02-15 12:40:52 +01:00
[ Service ]
Type = oneshot
2022-09-02 20:14:53 +02:00
ExecStart = / bin / bash - c " echo bar >> {self.output_file} "
2020-02-03 13:07:45 +01:00
ExecStart = / bin / sleep 3
2022-09-02 20:14:53 +02:00
ExecStart = / bin / bash - c " echo foo >> {self.output_file} "
'''
2017-02-15 12:40:52 +01:00
self . unit_files [ UnitFileChange . COMMAND_ADDED_BEFORE ] = unit_file_content
2022-09-02 20:14:53 +02:00
unit_file_content = f '''
2017-02-15 12:40:52 +01:00
[ Service ]
Type = oneshot
2020-02-03 13:07:45 +01:00
ExecStart = / bin / sleep 3
2022-09-02 20:14:53 +02:00
ExecStart = / bin / bash - c " echo foo >> {self.output_file} "
ExecStart = / bin / bash - c " echo bar >> {self.output_file} "
'''
2017-02-15 12:40:52 +01:00
self . unit_files [ UnitFileChange . COMMAND_ADDED_AFTER ] = unit_file_content
2022-09-02 20:14:53 +02:00
unit_file_content = f '''
2017-02-15 12:40:52 +01:00
[ Service ]
Type = oneshot
2022-09-02 20:14:53 +02:00
ExecStart = / bin / bash - c " echo baz >> {self.output_file} "
2020-02-03 13:07:45 +01:00
ExecStart = / bin / sleep 3
2022-09-02 20:14:53 +02:00
ExecStart = / bin / bash - c " echo foo >> {self.output_file} "
ExecStart = / bin / bash - c " echo bar >> {self.output_file} "
'''
2017-02-15 12:40:52 +01:00
self . unit_files [ UnitFileChange . COMMAND_INTERLEAVED ] = unit_file_content
2022-09-02 20:14:53 +02:00
unit_file_content = f '''
2017-02-15 12:40:52 +01:00
[ Service ]
Type = oneshot
2022-09-02 20:14:53 +02:00
ExecStart = / bin / bash - c " echo bar >> {self.output_file} "
ExecStart = / bin / bash - c " echo baz >> {self.output_file} "
2022-09-13 20:07:22 +02:00
'''
2017-02-15 12:40:52 +01:00
self . unit_files [ UnitFileChange . REMOVAL ] = unit_file_content
def reload ( self ) :
subprocess . check_call ( [ ' systemctl ' , ' daemon-reload ' ] )
def write_unit_file ( self , unit_file_change ) :
if not isinstance ( unit_file_change , UnitFileChange ) :
raise ValueError ( ' Unknown unit file change ' )
content = self . unit_files [ unit_file_change ]
2022-09-02 20:14:53 +02:00
with open ( self . unitfile_path , ' w ' , encoding = ' utf-8 ' ) as f :
2017-02-15 12:40:52 +01:00
f . write ( content )
self . reload ( )
def check_output ( self , expected_output ) :
2022-09-02 20:06:12 +02:00
for _ in range ( 15 ) :
try :
2022-09-02 20:14:53 +02:00
with open ( self . output_file , ' r ' , encoding = ' utf-8 ' ) as log :
2022-09-02 20:06:12 +02:00
output = log . read ( )
self . assertEqual ( output , expected_output )
return
except IOError :
pass
time . sleep ( 1 )
2017-02-15 12:40:52 +01:00
2022-09-02 20:14:53 +02:00
self . fail ( f ' Timed out while waiting for the output file { self . output_file } to appear ' )
2017-02-15 12:40:52 +01:00
def setup_unit ( self ) :
self . write_unit_file ( UnitFileChange . NO_CHANGE )
subprocess . check_call ( [ ' systemctl ' , ' --job-mode=replace ' , ' --no-block ' , ' start ' , self . unit ] )
2020-02-03 13:07:45 +01:00
time . sleep ( 1 )
2017-02-15 12:40:52 +01:00
def test_no_change ( self ) :
expected_output = ' foo \n '
self . setup_unit ( )
self . reload ( )
self . check_output ( expected_output )
def test_swapped ( self ) :
self . setup_unit ( )
self . write_unit_file ( UnitFileChange . LINES_SWAPPED )
self . reload ( )
self . assertTrue ( not os . path . exists ( self . output_file ) )
def test_added_before ( self ) :
expected_output = ' foo \n '
self . setup_unit ( )
self . write_unit_file ( UnitFileChange . COMMAND_ADDED_BEFORE )
self . reload ( )
self . check_output ( expected_output )
def test_added_after ( self ) :
expected_output = ' foo \n bar \n '
self . setup_unit ( )
self . write_unit_file ( UnitFileChange . COMMAND_ADDED_AFTER )
self . reload ( )
self . check_output ( expected_output )
def test_interleaved ( self ) :
expected_output = ' foo \n bar \n '
self . setup_unit ( )
self . write_unit_file ( UnitFileChange . COMMAND_INTERLEAVED )
self . reload ( )
self . check_output ( expected_output )
def test_removal ( self ) :
self . setup_unit ( )
self . write_unit_file ( UnitFileChange . REMOVAL )
self . reload ( )
self . assertTrue ( not os . path . exists ( self . output_file ) )
2017-08-25 15:36:10 +02:00
def test_issue_6533 ( self ) :
unit = " test-issue-6533.service "
2022-09-02 20:14:53 +02:00
unitfile_path = f " /run/systemd/system/ { unit } "
2017-08-25 15:36:10 +02:00
content = '''
[ Service ]
ExecStart = / bin / sleep 5
'''
2022-09-02 20:14:53 +02:00
with open ( unitfile_path , ' w ' , encoding = ' utf-8 ' ) as f :
2017-08-25 15:36:10 +02:00
f . write ( content )
self . reload ( )
subprocess . check_call ( [ ' systemctl ' , ' --job-mode=replace ' , ' --no-block ' , ' start ' , unit ] )
time . sleep ( 2 )
content = '''
[ Service ]
ExecStart = / bin / sleep 5
ExecStart = / bin / true
'''
2022-09-02 20:14:53 +02:00
with open ( unitfile_path , ' w ' , encoding = ' utf-8 ' ) as f :
2017-08-25 15:36:10 +02:00
f . write ( content )
self . reload ( )
time . sleep ( 5 )
2022-09-02 20:06:12 +02:00
self . assertTrue ( subprocess . call ( " journalctl -b _PID=1 | grep -q ' Freezing execution ' " , shell = True ) != 0 )
2017-08-25 15:36:10 +02:00
2017-02-15 12:40:52 +01:00
def tearDown ( self ) :
for f in [ self . output_file , self . unitfile_path ] :
try :
os . remove ( f )
except OSError :
# ignore error if log file doesn't exist
pass
self . reload ( )
if __name__ == ' __main__ ' :
2020-02-03 13:07:45 +01:00
unittest . main ( testRunner = unittest . TextTestRunner ( stream = sys . stdout , verbosity = 3 ) )