1
0
mirror of https://pagure.io/fedora-qa/os-autoinst-distri-fedora.git synced 2024-12-18 08:33:08 +00:00

Do the EOL checks through a Python script.

The complexity of the Perl test script went pretty high
when all the checks EOL checks were done there, so we
moved the functionality to an external Python script
that does the thing and returns a code based on the
result.
This commit is contained in:
Lukas Ruzicka 2024-12-12 16:24:41 +01:00
parent e4aeaa93b2
commit 908cd657a9
2 changed files with 220 additions and 135 deletions

209
check-release.py Executable file
View File

@ -0,0 +1,209 @@
#!/usr/bin/python3
# Copyright Red Hat
#
# This file is part of os-autoinst-distri-fedora.
#
# os-autoinst-distri-fedora 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 2 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, see <http://www.gnu.org/licenses/>.
#
# Author: Lukas Ruzicka <lruzicka@redhat.com>
"""
This script provides a simple test for Eof of Life dates on Fedora.
You can use it for two types of testing. The first test checks that
the SUPPORT_END value is at least a year ahead (in the time of testing),
which it should be. The second test checks if the End of Life date is
consisant across the three sources, the os-release file, Bodhi, and Fedora
Schedule.
When the test passes, it returns 0, otherwise there is one of the error codes.
"""
import argparse
import sys
from datetime import date, datetime, timedelta
import requests
VERBOSE = False
RESULT = 100
def cli():
"""Return the CLI arguments."""
parser = argparse.ArgumentParser(
description="Fedora '/etc/os-release' support date validator."
)
parser.add_argument(
"--test",
"-t",
type=str,
required=True,
help="Test to perform [future, compare]",
)
parser.add_argument(
"--release",
"-r",
type=str,
required=False,
help="Fedora release number (42, 43, ...)",
)
parser.add_argument(
"--verbose",
"-v",
action="store_true",
help="Prints detailed info on the screen.",
)
args = parser.parse_args()
return args
def log(*args, **kwargs):
"""Print out messages on CLI if VERBOSE."""
if VERBOSE:
print(*args, **kwargs)
else:
pass
def epochdate(epoch: int) -> date:
"""Return the date object calculated from the epoch integer."""
converted = datetime.fromtimestamp(epoch)
return converted.date()
def isodate(iso: str) -> date:
"""Return the date object calculated from the ISO format."""
converted = date.fromisoformat(iso)
return converted
def get_file_support() -> date:
"""Returns the support date from the os-release file."""
with open("/etc/os-release", "r", encoding="utf-8") as release:
lines = release.readlines()
log("The /etc/os-release successfully read.")
support_day = epochdate(0)
for line in lines:
if "SUPPORT_END" in line:
_, value = line.split("=")
value = value.strip()
if value:
support_day = isodate(value)
return support_day
def support_date_in_future(eol: date) -> bool:
"""This function checks the support date from the os-release
file, compares it with the current system date and tests if
the os-release support date lies at least 12 months in the future."""
# Get the necessary values from the operating system.
today = datetime.today().date()
log("Current date on tested system is:", today)
tomorrow = today + timedelta(days=365)
log("Minimal SUPPORT_END calculated from system time is:", tomorrow)
log("Real /etc/os-release SUPPORT_END is:", eol)
# Test if the support end date is in the future.
result = False
if eol >= tomorrow:
log("Real SUPPORT_END is one year in the future.")
result = 0
else:
log("Real SUPPORT_END is NOT one year in the future.")
result = 1
return result
def compare_eol_dates(release: int, eol: date) -> bool:
"""This function checks the support date on Fedora Schedule, Bodhi
and the os-release file and compares them whether they are the same
and fails if they are not."""
log("The EOL date shown by the os-release file is:", eol.isoformat())
# Get the Bodhi EOL date
bodhi_response = requests.get(
f"https://bodhi.fedoraproject.org/releases/F{release}", timeout=60
)
bodhi = bodhi_response.json()
# Only convert the date if it is present, otherwise record 0.
if bodhi["eol"]:
bodhi_eol = isodate(bodhi["eol"])
else:
bodhi_eol = epochdate(0)
log("The EOL date shown by Bodhi is:", bodhi_eol.isoformat())
# Get the Schedule EOL date
schedule_response = requests.get(
f"https://fedorapeople.org/groups/schedule/f-{release}/"
f"f-{release}-key.json",
timeout=60,
)
schedule = schedule_response.json()
tasks = schedule["tasks"][0]["tasks"][0]["tasks"]
schedule_eol = epochdate(0)
for task in tasks:
if "End of Life" in task["name"]:
schedule_eol = epochdate(int(task["end"]))
break
log("The EOL date shown by Fedora Schedule is:", schedule_eol.isoformat())
# Compare the dates
result = None
if eol == bodhi_eol and eol == schedule_eol:
log("All EOL dates have the same value.")
result = 0
elif eol == bodhi_eol:
log("The os-release matches Bodhi but Fedora Schedule is different.")
result = 1
elif eol == schedule_eol:
log("The os-release matches Fedora Schedule but Bodhi is different.")
result = 2
elif bodhi_eol == schedule_eol:
log("Bodhi matches Fedora Schedule, but os-release is different.")
result = 3
else:
log("All EOL dates have different values.")
result = 4
return result
arguments = cli()
VERBOSE = arguments.verbose
os_release_eol = get_file_support()
RESULT = 100
if arguments.test == "compare":
if not arguments.release:
print("Please, run again with --release option. Finishing.")
sys.exit(5)
RESULT = compare_eol_dates(arguments.release, os_release_eol)
else:
RESULT = support_date_in_future(os_release_eol)
if RESULT != 0:
log("Test failed.")
else:
log("Test passed.")
# Sysexit based on the results.
sys.exit(RESULT)

View File

@ -14,136 +14,11 @@ sub strip_marks {
return $string; return $string;
} }
sub json_to_hash { sub download_python_tests {
# Take a string formed as json and return a valid Perl hash # Download the Python test script and change mode to rwx,rx,rx.
# for further processing. assert_script_run("curl https://pagure.io/fedora-qa/os-autoinst-distri-fedora/raw/os-release-addon/f/check-release.py -o ~/check-release.py --retry 10 --retry-delay 2", timeout => 60);
my $json = shift; assert_script_run("chmod 755 ~/check-release.py", timeout => 20);
my $hash = decode_json($json); sleep(5);
# If the json is not decoded correctly, fail.
die("Failed to parse JSON: $@") if ($@);
return $hash;
}
sub date_to_epoch {
# Take the date as YYYY-MM-DD and convert it into epoch.
my $dstring = shift;
my $date = Time::Piece->strptime($dstring, '%Y-%m-%d');
my $epoch = $date->epoch;
return $epoch;
}
sub epoch_to_date {
# Take the epoch and return YYYY-MM-DD.
my $epoch = shift;
my $time = localtime($epoch);
my $date = $time->strftime('%Y-%m-%d');
return $date;
}
sub get_bodhi_eol {
# Load the Bodhi json file and return the EOL epoch.
my $ver = shift;
# Use the openQA script output to read the json file. We use jq to make
# sure that the content is a valid json file.
my $json = script_output("cat ~/version_data/bodhi-$ver.json | jq -c");
my $bodhi = json_to_hash($json);
my $eol = $bodhi->{"eol"};
$eol = date_to_epoch($eol);
return $eol;
}
sub get_schedule_eol {
# Load the Fedora Schedule json file and return the EOL epoch.
my $ver = shift;
my $json = script_output("cat ~/version_data/schedule-$ver.json | jq -c");
my $schedule = json_to_hash($json);
my $eol;
# Identify the events and find the EOL field.
my $tasks = $schedule->{'tasks'}[0]{'tasks'}[0]{'tasks'};
my $eol;
foreach my $task (@$tasks) {
if ($task->{'name'} and $task->{'name'} =~ /End of Life/) {
$eol = $task->{'end'};
last;
}
}
# The EOL is already formed as epoch, just return it.
return $eol;
}
sub get_current_date {
# This returns the current date in as the epoch and YYYY-MM-DD.
# which we need to see if the EOL is correctly set in the future.
my $time = localtime;
my $dates = {};
$dates->{'date'} = $time->strftime('%Y-%m-%d');
$dates->{'epoch'} = date_to_epoch($dates->{'date'});
return $dates;
}
sub check_eol_in_year {
# This reads the EOL date from the /etc/os-release
# file and checks that it is at least a year in
# the future (when tested on non-published ISOs).
# Returns true if successful.
my $tested = shift;
$tested = date_to_epoch($tested);
my $dates = get_current_date();
my $current = $dates->{epoch};
# The EOL in the os-release.pm must be at least a year
# in the future, so we calculate the epoch difference
# between $tested and $current.
# An epoch year should be
# 1 * 60 (min) *60 (hour) *24 (day) *365 (year)
my $year = 1 * 60 * 60 * 24 * 365;
my $delta = $tested - $current;
my $bool = 1;
if ($delta < $year) {
$bool = 0;
}
return $bool;
}
sub check_eols_match {
# This takes the EOL dates from the /etc/os-release
# file and compares the value with those from
# Bodhi and Fedora Schedule and will succeed when they
# match each other.
my $tested = shift;
my $version = get_release_number();
my $bodhi = get_bodhi_eol($version);
$bodhi = epoch_to_date($bodhi);
my $schedule = get_schedule_eol($version);
$schedule = epoch_to_date($schedule);
# Let's set the return code to 0 to indicate that none of the EOL date
# can be matched with another one.
my $rcode = 0;
my $overall = 1;
# Start comparisons among the EOL dates.
if ($bodhi eq $schedule and $bodhi eq $tested) {
$rcode = 1;
}
elsif ($bodhi eq $schedule) {
$rcode = 2;
}
elsif ($tested eq $bodhi) {
$rcode = 3;
}
elsif ($tested eq $schedule) {
$rcode = 4;
}
# If the EOL dates do not match each other, consider this a test failure.
$overall = 0 if ($rcode != 1);
my $return_codes = {
0 => "No EOL dates do match:\n\tos-release: $tested\n\tBodhi: $bodhi\n\tSchedule: $schedule",
2 => "The os-release doesn't match Bodhi or Schedule, but they match each other:\n\tos-release: $tested\n\tBodhi: $bodhi\n\tSchedule: $schedule",
3 => "The os-release file matches Bodhi but Schedule differs:\n\tos-release: $tested\n\tBodhi: $bodhi\n\tSchedule: $schedule",
4 => "The os-release file matches Schedule but Bodhi differs:\n\tos-release: $tested\n\tBodhi: $bodhi\n\tSchedule: $schedule",
1 => "All EOL dates match:\n\tos-release: $tested\n\tBodhi: $bodhi\n\tSchedule: $schedule"
};
my $result = [$overall, $return_codes->{$rcode}];
return $result;
} }
sub run { sub run {
@ -316,15 +191,16 @@ sub run {
} }
# Download Python test script to run the tests.
download_python_tests();
# Test for EOL date in the distant future. # Test for EOL date in the distant future.
my $os_release_eol = $content{'SUPPORT_END'}; my $os_release_eol = $content{'SUPPORT_END'};
my $result = check_eol_in_year($os_release_eol); my $result = script_run("~/check-release.py --test future --verbose");
my $current = get_current_date(); rec_log("The SUPPORT_END date is $os_release_eol which is at least a year ahead in time.", $result == 0, $failref);
rec_log("The SUPPORT_END date is $os_release_eol which is at least a year ahead in time (now is $current->{date})", $result == 1, $failref);
# Test for EOL dates match each other. # Test for EOL dates match each other.
$result = check_eols_match($os_release_eol); $result = script_run("~/check-release.py --test compare --release $version_id --verbose");
rec_log($result->[1], $result->[0] == 1, $failref); rec_log("The End of Life dates match each other (local, Bodhi, Schedule)", $result == 0, $failref);
# Check for fails, count them, collect their messages and die if something was found. # Check for fails, count them, collect their messages and die if something was found.
my $failcount = scalar @fails; my $failcount = scalar @fails;