2019-05-16 13:01:52 +00:00
use base "installedtest" ;
use strict ;
2024-11-26 13:39:07 +00:00
use JSON ;
use Time::Piece ;
2019-05-16 13:01:52 +00:00
use testapi ;
use utils ;
# This test checks that the descriptions in /etc/os-release file are correct and that they
# match the current version.
sub strip_marks {
2022-07-28 20:32:57 +00:00
# Remove the quotation marks from the string:
my $ string = shift ;
$ string =~ tr /"/ / d ;
return $ string ;
2019-05-16 13:01:52 +00:00
}
2024-11-26 13:39:07 +00:00
sub json_to_hash {
# Take a string formed as json and return a valid Perl hash
# for further processing.
my $ json = shift ;
my $ hash = decode_json ( $ json ) ;
# 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 ;
}
2019-05-16 13:01:52 +00:00
sub run {
2022-07-28 20:32:57 +00:00
# First, let us define some variables needed to run the program.
my $ self = shift ;
# The file to be checked
my $ filename = '/etc/os-release' ;
# Read the content of the file to compare. Let us parse the file
# and create a hash with those values, so that we can easily access
# them and assert them.
my $ infile = script_output "cat /etc/os-release" ;
my @ infile = split /\n/ , $ infile ;
my % content = ( ) ;
foreach ( @ infile ) {
chomp $ _ ;
my ( $ key , $ value ) = split /=/ , $ _ ;
$ content { $ key } = $ value ;
}
# Now, we have all the data ready and we can start testing, first let us get
# correct variables to compare the system data with.
# First, we know the basic stuff
# Should be "fedora"
my $ id = get_var ( "DISTRI" ) ;
# extract expected version components from ISO name for canned variants,
# which have their os-release rewritten by rpm-ostree, see:
# https://github.com/projectatomic/rpm-ostree/blob/master/docs/manual/treefile.md
# we use the ISO name because rpm-ostree uses elements from the compose
# ID for nightlies, but from the label for candidate composes; BUILD
# always gives us the compose ID, but the ISO name contains the compose
# ID for nightlies but the label for candidate composes, so it works for
# our purposes here.
my $ isovar = get_var ( "ISO" ) ;
# Split the ISO variable at "-" and read second-to-last (release
# number) and last (compose ID: date and respin, label: major and
# minor) fields.
my ( $ cannedver , $ cannednum ) = ( split /-/ , $ isovar ) [ - 2 , - 1 ] ;
# Get rid of the ".iso" part of the tag.
$ cannednum =~ s/\.iso//g ;
2024-03-14 15:25:26 +00:00
# also get rid of the arch, which osbuild puts in here
my $ arch = get_var ( "ARCH" ) ;
$ cannednum =~ s/\.$arch//g ;
2022-07-28 20:32:57 +00:00
# Now, we merge the fields into one expression to create the correct canned tag
# that will contain both the version number and the build number.
my $ cannedtag = "$cannedver.$cannednum" ;
# If this is a CoreOS build, though, throw all that away and
# just use the build version
my $ build = get_var ( "BUILD" ) ;
if ( $ build =~ /^Fedora-CoreOS/ ) {
$ cannedtag = ( split /-/ , $ build ) [ - 1 ] ;
}
my $ name = ucfirst ( $ id ) ;
2022-12-13 19:48:44 +00:00
my $ fullname = $ name . " Linux" ;
2022-07-28 20:32:57 +00:00
my $ rawrel = get_var ( "RAWREL" , '' ) ;
# Should be the version number or Rawhide.
my $ version_id = get_var ( "VERSION" ) ;
# IoT has a branch that acts more or less like Rawhide, but has
# its version as the Rawhide release number, not 'Rawhide'. This
# handles that
$ version_id = 'Rawhide' if ( $ version_id eq $ rawrel ) ;
my $ varstr = spell_version_number ( $ version_id ) ;
my $ target = lc ( $ version_id ) ;
$ version_id = $ rawrel if ( $ version_id eq "Rawhide" ) ;
# the 'generic' os-release in fedora-release has no VARIANT or
# VARIANT_ID and the string used in values like VERSION, that in other
# cases is the VARIANT, is 'Rawhide' for Rawhide and the spelt version
# number for other releases. These are the values we'll see for an
# Everything image.
my $ variant_id = "" ;
my $ variant = "generic" ;
# now replace the values with the correct ones if we are testing a
# subvariant that maps to a known variant
my $ subvariant = get_var ( 'SUBVARIANT' ) ;
my % variants = (
Server = > [ "server" , "Server Edition" ] ,
Workstation = > [ "workstation" , "Workstation Edition" ] ,
AtomicHost = > [ "atomic.host" , "Atomic Host" ] ,
CoreOS = > [ "coreos" , "CoreOS" ] ,
KDE = > [ "kde" , "KDE Plasma" ] ,
Silverblue = > [ "silverblue" , "Silverblue" ] ,
IoT = > [ "iot" , "IoT Edition" ] ,
) ;
if ( exists ( $ variants { $ subvariant } ) ) {
( $ variant_id , $ variant ) = @ { $ variants { $ subvariant } } ;
$ varstr = $ variant ;
}
# If fedora-release-common release starts with a 0, we'll have
# "Prerelease" in varstr
my $ reltag = script_output 'rpm -q fedora-release-common --qf "%{RELEASE}\n"' ;
if ( index ( $ reltag , "0." ) == 0 ) {
$ varstr . = " Prerelease" ;
# ...however, we shouldn't just wave this through if we're
# an RC candidate or update compose, those should never be
# done with a 0.x fedora-release-common. so let's blow up
# here if so. unless it's IoT, because IoT is weird
my $ label = get_var ( "LABEL" ) ;
if ( $ label =~ /^(RC|Update)-/ && $ subvariant ne "IoT" ) {
die "RC candidate or update compose should not have 0.x versioned fedora-release!" ;
2019-05-16 13:01:52 +00:00
}
2022-07-28 20:32:57 +00:00
}
my $ version = "$version_id ($varstr)" ;
# for canned variants, we need to form a different string here by using
# the above created cannedtag. See earlier comment
if ( get_var ( "CANNED" ) ) {
$ version = "$cannedtag ($varstr)" ;
}
my $ platform_id = "platform:f$version_id" ;
my $ pretty = "$fullname $version_id ($varstr)" ;
# Same problem is when testing the PRETTY_NAME.
if ( get_var ( "CANNED" ) ) {
$ pretty = "$fullname $cannedtag ($varstr)" ;
# ...and FCOS uses a different format, sigh
2020-09-02 23:22:20 +00:00
if ( $ build =~ /^Fedora-CoreOS/ ) {
2022-07-28 20:32:57 +00:00
$ pretty = "Fedora CoreOS $cannedtag" ;
2019-05-16 13:01:52 +00:00
}
2022-07-28 20:32:57 +00:00
}
#Now. we can start testing the real values from the installed system.
my @ fails = ( ) ;
my $ failref = \ @ fails ;
# Test for name
my $ strip = strip_marks ( $ content { 'NAME' } ) ;
rec_log "NAME should be $fullname and is $strip" , $ strip eq $ fullname , $ failref ;
# Test for version.
$ strip = strip_marks ( $ content { 'VERSION' } ) ;
rec_log "VERSION should be $version and is $strip" , $ strip eq $ version , $ failref ;
# Test for version_id
rec_log "VERSION_ID should be $version_id and is $content{'VERSION_ID'}" , $ content { 'VERSION_ID' } eq $ version_id , $ failref ;
# Test for platform_id
$ strip = strip_marks ( $ content { 'PLATFORM_ID' } ) ;
rec_log "PLATFORM_ID should be $platform_id and is $strip" , $ strip eq $ platform_id , $ failref ;
# Test for pretty name
$ strip = strip_marks ( $ content { 'PRETTY_NAME' } ) ;
rec_log "PRETTY_NAME should be $pretty and is $strip" , $ strip eq $ pretty , $ failref ;
# Test for RH Bugzilla Product
$ strip = strip_marks ( $ content { 'REDHAT_BUGZILLA_PRODUCT' } ) ;
rec_log "REDHAT_BUGZILLA_PRODUCT should be $name and is $strip" , $ strip eq $ name , $ failref ;
# Test for RH Bugzilla Product Version
rec_log "REDHAT_BUGZILLA_PRODUCT_VERSION should be $target and is $content{'REDHAT_BUGZILLA_PRODUCT_VERSION'}" , $ content { 'REDHAT_BUGZILLA_PRODUCT_VERSION' } eq $ target , $ failref ;
# Test for RH Support Product
$ strip = strip_marks ( $ content { 'REDHAT_SUPPORT_PRODUCT' } ) ;
rec_log "REDHAT_SUPPORT_PRODUCT should be $name and is $strip" , $ strip eq $ name , $ failref ;
# Test for RH Support Product Version
rec_log "REDHAT_SUPPORT_PRODUCT_VERSION should be $target and is $content{'REDHAT_SUPPORT_PRODUCT_VERSION'}" , $ content { 'REDHAT_SUPPORT_PRODUCT_VERSION' } eq $ target , $ failref ;
# Test for Variant but only in case of Server or Workstation
if ( $ variant ne "generic" ) {
$ strip = strip_marks ( $ content { 'VARIANT' } ) ;
rec_log "VARIANT should be $variant and is $strip" , $ strip eq $ variant , $ failref ;
# Test for VARIANT_ID
rec_log "VARIANT_ID should be $variant_id and is $content{'VARIANT_ID'}" , $ content { 'VARIANT_ID' } eq $ variant_id , $ failref ;
}
else {
print "VARIANT was not tested because the compose is not Workstation or Server Edition.\n" ;
print "VARIANT_ID was not tested because the compose is not Workstation or Server Edition.\n" ;
}
2024-11-26 13:39:07 +00:00
# Test for EOL date in the distant future.
my $ os_release_eol = $ content { 'SUPPORT_END' } ;
my $ result = check_eol_in_year ( $ os_release_eol ) ;
my $ current = get_current_date ( ) ;
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.
$ result = check_eols_match ( $ os_release_eol ) ;
rec_log ( $ result - > [ 1 ] , $ result - > [ 0 ] == 1 , $ failref ) ;
2022-07-28 20:32:57 +00:00
# Check for fails, count them, collect their messages and die if something was found.
my $ failcount = scalar @ fails ;
script_run "echo \"There were $failcount failures in total.\" >> /tmp/os-release.log" ;
upload_logs "/tmp/os-release.log" , failok = > 1 ;
my $ failmessages = "" ;
foreach my $ fail ( @ fails ) {
$ failmessages . = "\n" . $ fail ;
}
die $ failmessages if ( $ failcount > 0 ) ;
2019-05-16 13:01:52 +00:00
}
sub test_flags {
return { always_rollback = > 1 } ;
}
1 ;
# vim: set sw=4 et: