Skip to content

Commit 973cfdc

Browse files
authored
Remove partially installer image when image install failed. (sonic-net#3712)
Remove partially installer image when image install failed. What I did When install image failed, partially installed image not removed, which may cause disk full issue on small disk device. How I did it Handle install image SystemExit exception and uninstall image. How to verify it Manually verify. Pass all test case
1 parent 97c20cc commit 973cfdc

File tree

2 files changed

+62
-1
lines changed

2 files changed

+62
-1
lines changed

sonic_installer/main.py

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import configparser
22
import os
33
import re
4+
import shutil
45
import subprocess
56
import sys
67
import time
@@ -591,7 +592,18 @@ def install(url, force, skip_platform_check=False, skip_migration=False, skip_pa
591592

592593
echo_and_log("Installing image {} and setting it as default...".format(binary_image_version))
593594
with SWAPAllocator(not skip_setup_swap, swap_mem_size, total_mem_threshold, available_mem_threshold):
594-
bootloader.install_image(image_path)
595+
try:
596+
bootloader.install_image(image_path)
597+
except SystemExit as e:
598+
# When install image failed with exception, image is partial installed
599+
# Fully installed image will update SONiC environment by update_sonic_environment
600+
# For partial installed image, only need delete image folder
601+
echo_and_log('Image install failed with exception: {}'.format(e))
602+
echo_and_log('Delete partial installed image: {}'.format(binary_image_version))
603+
new_image_dir = bootloader.get_image_path(binary_image_version)
604+
shutil.rmtree(new_image_dir)
605+
raise
606+
595607
# Take a backup of current configuration
596608
if skip_migration:
597609
echo_and_log("Skipping configuration migration as requested in the command option.")

tests/test_sonic_installer.py

+49
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,52 @@ def test_runtime_exception(mock_popen):
147147
assert '\nSTDERR:\nFailed' in sre.value.notes, "Invalid STDERR"
148148

149149
assert all(v in str(sre.value) for v in ['test.sh', 'Running', 'Failed']), "Invalid message"
150+
151+
152+
@patch("sonic_installer.main.SWAPAllocator")
153+
@patch("sonic_installer.main.get_bootloader")
154+
@patch("sonic_installer.main.run_command_or_raise")
155+
@patch("sonic_installer.main.run_command")
156+
@patch('shutil.rmtree')
157+
def test_install_failed(rmtree, run_command, run_command_or_raise, get_bootloader, swap, fs):
158+
""" This test covers the "sonic-installer" install image failed handling. """
159+
160+
sonic_image_filename = "sonic.bin"
161+
current_image_version = "image_1"
162+
new_image_version = "image_2"
163+
new_image_folder = f"/images/{new_image_version}"
164+
image_docker_folder = os.path.join(new_image_folder, "docker")
165+
mounted_image_folder = f"/tmp/image-{new_image_version}-fs"
166+
dockerd_opts = ["--iptables=false", "--bip=1.1.1.1/24"]
167+
168+
# Setup mock files needed for our test scenario
169+
fs.create_file(sonic_image_filename)
170+
fs.create_dir(image_docker_folder)
171+
fs.create_dir(os.path.join(mounted_image_folder, "usr/lib/docker/docker.sh"))
172+
fs.create_file("/var/run/docker.pid", contents="15")
173+
fs.create_file("/proc/15/cmdline", contents="\x00".join(["dockerd"] + dockerd_opts))
174+
175+
# Setup bootloader mock
176+
mock_bootloader = Mock()
177+
mock_bootloader.get_binary_image_version = Mock(return_value=new_image_version)
178+
mock_bootloader.get_installed_images = Mock(return_value=[current_image_version])
179+
mock_bootloader.get_image_path = Mock(return_value=new_image_folder)
180+
mock_bootloader.verify_image_sign = Mock(return_value=True)
181+
182+
def mock_install_image(arg):
183+
sys.exit(1)
184+
185+
mock_bootloader.install_image = mock_install_image
186+
187+
@contextmanager
188+
def rootfs_path_mock(path):
189+
yield mounted_image_folder
190+
191+
mock_bootloader.get_rootfs_path = rootfs_path_mock
192+
193+
get_bootloader.return_value = mock_bootloader
194+
195+
# Invoke CLI command
196+
runner = CliRunner()
197+
result = runner.invoke(sonic_installer.commands["install"], [sonic_image_filename, "-y"])
198+
print(result.output)

0 commit comments

Comments
 (0)