diff --git a/core/src/main/java/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor.java b/core/src/main/java/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor.java new file mode 100644 index 0000000000..e0579b9090 --- /dev/null +++ b/core/src/main/java/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor.java @@ -0,0 +1,278 @@ +/* + * The MIT License + * + * Copyright 2023 Mark Waite. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package jenkins.monitor; + +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import hudson.Extension; +import hudson.model.AdministrativeMonitor; +import hudson.security.Permission; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.time.LocalDate; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import jenkins.model.Jenkins; +import net.sf.json.JSONArray; +import net.sf.json.JSONObject; +import org.apache.commons.io.IOUtils; +import org.jenkinsci.Symbol; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.DoNotUse; +import org.kohsuke.accmod.restrictions.NoExternalUse; +import org.kohsuke.stapler.HttpRedirect; +import org.kohsuke.stapler.HttpResponse; +import org.kohsuke.stapler.HttpResponses; +import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.interceptor.RequirePOST; + +@Extension +@Restricted(NoExternalUse.class) +@Symbol("operatingSystemEndOfLife") +public class OperatingSystemEndOfLifeAdminMonitor extends AdministrativeMonitor { + + static final Logger LOGGER = Logger.getLogger(OperatingSystemEndOfLifeAdminMonitor.class.getName()); + + /** + * Allow tests to disable the end of life monitor without a JenkinsRule. + */ + boolean ignoreEndOfLife = false; + + private LocalDate warningsStartDate = LocalDate.now().plusYears(10); + private boolean afterEndOfLifeDate = false; + private String operatingSystemName = System.getProperty("os.name", "Unknown"); + private String endOfLifeDate = "2099-12-31"; + private String documentationUrl = "https://www.jenkins.io/redirect/operating-system-end-of-life"; + + public OperatingSystemEndOfLifeAdminMonitor(String id) throws IOException { + super(id); + fillOperatingSystemList(); + } + + public OperatingSystemEndOfLifeAdminMonitor() throws IOException { + fillOperatingSystemList(); + } + + private void fillOperatingSystemList() throws IOException { + if (Jenkins.getInstanceOrNull() != null && !isEnabled()) { + /* If not enabled, do not read the data files or perform any checks */ + LOGGER.log(Level.FINEST, "Operating system end of life monitor is not enabled, reading no data"); + return; + } + ClassLoader cl = getClass().getClassLoader(); + URL localOperatingSystemData = cl.getResource("jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor/end-of-life-data.json"); + String initialOperatingSystemJson = IOUtils.toString(localOperatingSystemData.openStream(), StandardCharsets.UTF_8); + readOperatingSystemList(initialOperatingSystemJson); + } + + /* Package protected for testing */ + void readOperatingSystemList(String initialOperatingSystemJson) throws IOException { + JSONArray systems = JSONArray.fromObject(initialOperatingSystemJson); + if (systems.isEmpty()) { + throw new IOException("Empty data set"); + } + for (Object systemObj : systems) { + if (!(systemObj instanceof JSONObject)) { + throw new IOException("Wrong object type in data file"); + } + JSONObject system = (JSONObject) systemObj; + + if (!system.has("pattern")) { + throw new IOException("Missing pattern in definition file"); + } + String pattern = system.getString("pattern"); + + if (!system.has("endOfLife")) { + throw new IOException("No end of life date for " + pattern); + } + LocalDate endOfLife = LocalDate.parse(system.getString("endOfLife")); + + /* Start date defaults to 6 months before end of life */ + LocalDate startDate = system.has("start") ? LocalDate.parse(system.getString("start")) : endOfLife.minusMonths(6); + + File dataFile = getDataFile(system); + + LOGGER.log(Level.FINEST, "Pattern {0} starts {1} and reaches end of life {2} from file {3}", + new Object[]{pattern, startDate, endOfLife, dataFile}); + + String name = readOperatingSystemName(dataFile, pattern); + if (name.isEmpty()) { + LOGGER.log(Level.FINE, "Pattern {0} did not match from file {1}", + new Object[]{pattern, dataFile}); + continue; + } + + if (startDate.isBefore(warningsStartDate)) { + warningsStartDate = startDate; + LOGGER.log(Level.FINE, "Warnings start date is now {0}", warningsStartDate); + } + + LOGGER.log(Level.FINE, "Matched operating system {0}", name); + if (startDate.isBefore(LocalDate.now())) { + this.operatingSystemName = name; + this.documentationUrl = buildDocumentationUrl(this.operatingSystemName); + this.endOfLifeDate = endOfLife.toString(); + if (endOfLife.isBefore(LocalDate.now())) { + LOGGER.log(Level.FINE, "Operating system {0} is after end of life {1}", + new Object[]{name, endOfLife}); + afterEndOfLifeDate = true; + } else { + LOGGER.log(Level.FINE, "Operating system {0} started warnings {1} and reaches end of life {2}", + new Object[]{name, startDate, endOfLife}); + } + } + } + } + + @SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", + justification = "File path defined in war file, not by user") + @CheckForNull + private File getDataFile(@NonNull JSONObject system) { + /* dataFile defaults to /etc/os-release */ + String fileName = "/etc/os-release"; + if (system.has("file")) { + fileName = system.getString("file"); + } + File dataFile = new File(fileName); + return dataFile; + } + + /* Package protected for testing */ + @NonNull + String readOperatingSystemName(File dataFile, @NonNull String patternStr) { + if (dataFile == null || !dataFile.exists()) { + return ""; + } + Pattern pattern = Pattern.compile("^PRETTY_NAME=[\"](" + patternStr + ".*)[\"]"); + String name = ""; + try { + List lines = Files.readAllLines(dataFile.toPath()); + for (String line : lines) { + Matcher matcher = pattern.matcher(line); + if (matcher.matches()) { + name = matcher.group(1); + } + } + } catch (IOException ioe) { + LOGGER.log(Level.SEVERE, "File read exception", ioe); + } + return name; + } + + @NonNull + public String getOperatingSystemName() { + return operatingSystemName; + } + + @NonNull + public String getEndOfLifeDate() { + return endOfLifeDate; + } + + public boolean getAfterEndOfLifeDate() { + return afterEndOfLifeDate; + } + + @NonNull + public String getDocumentationUrl() { + return documentationUrl; + } + + /* Package protected for tests */ + @NonNull + String readDocumentationUrl(File dataFile, @NonNull String patternStr) { + if (dataFile == null || !dataFile.exists()) { + return ""; + } + String operatingSystemName = readOperatingSystemName(dataFile, patternStr); + return buildDocumentationUrl(operatingSystemName); + } + + private String buildDocumentationUrl(String operatingSystemName) { + String scheme = "https"; + String hostName = "www.jenkins.io"; + String path = "/redirect/operating-system-end-of-life"; + String query = "q=" + operatingSystemName.replace(" ", "-").replace("/", "-").replace("(", "").replace(")", ""); + + String url = documentationUrl; + try { + URI documentationURI = new URI(scheme, hostName, path, query, null); + url = documentationURI.toString(); + } catch (URISyntaxException e) { + url = scheme + "://" + hostName + path; + } + return url; + } + + /* + * Send user to the right place depending on "yes" or "no". + */ + @Restricted(DoNotUse.class) // WebOnly + @RequirePOST + public HttpResponse doAct(@QueryParameter String no) throws IOException { + if (no != null) { // dismiss + Jenkins.get().checkPermission(Jenkins.ADMINISTER); + disable(true); + LOGGER.log(Level.FINE, "Disabled operating system end of life monitor"); + return HttpResponses.forwardToPreviousPage(); + } else { + LOGGER.log(Level.FINE, "Enabled operating system end of life monitor"); + return new HttpRedirect(documentationUrl); + } + } + + @Override + public boolean isActivated() { + if (ignoreEndOfLife) { + LOGGER.log(Level.FINE, "Not activated because ignoring end of life monitor"); + return false; + } + if (LocalDate.now().isBefore(warningsStartDate)) { + LOGGER.log(Level.FINE, "Not activated because it is before the start date {0}", warningsStartDate); + return false; + } + LOGGER.log(Level.FINEST, "Activated because it is after the warnings start date {0}", warningsStartDate); + return true; + } + + @Override + public Permission getRequiredPermission() { + return Jenkins.SYSTEM_READ; + } + + @Override + public String getDisplayName() { + return "Operating system end of life monitor"; + } +} diff --git a/core/src/main/resources/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor/description.jelly b/core/src/main/resources/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor/description.jelly new file mode 100644 index 0000000000..bb405e9319 --- /dev/null +++ b/core/src/main/resources/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor/description.jelly @@ -0,0 +1,4 @@ + + + ${%blurb} + diff --git a/core/src/main/resources/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor/description.properties b/core/src/main/resources/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor/description.properties new file mode 100644 index 0000000000..0228718c65 --- /dev/null +++ b/core/src/main/resources/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor/description.properties @@ -0,0 +1 @@ +blurb=Warns administrators when the controller operating system is approaching end of life and will be unsupported diff --git a/core/src/main/resources/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor/end-of-life-data.json b/core/src/main/resources/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor/end-of-life-data.json new file mode 100644 index 0000000000..d3136f94f1 --- /dev/null +++ b/core/src/main/resources/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor/end-of-life-data.json @@ -0,0 +1,98 @@ +[ + { + "pattern": "AlmaLinux.* 8", + "endOfLife": "2029-03-31" + }, + { + "pattern": "Alpine Linux v3.14", + "endOfLife": "2023-05-01" + }, + { + "pattern": "Alpine Linux v3.15", + "endOfLife": "2023-11-01" + }, + { + "pattern": "Alpine Linux v3.16", + "endOfLife": "2024-05-01" + }, + { + "pattern": "Alpine Linux v3.17", + "endOfLife": "2024-11-01" + }, + { + "pattern": "Alpine Linux v3.18", + "endOfLife": "2025-05-01" + }, + { + "pattern": "CentOS Linux.* 7", + "start": "2023-05-01", + "endOfLife": "2023-11-16" + }, + { + "pattern": "CentOS Linux.* 8", + "endOfLife": "2029-03-31" + }, + { + "pattern": "Debian.* 10", + "endOfLife": "2024-06-30" + }, + { + "pattern": "Debian.* 11", + "endOfLife": "2026-06-30" + }, + { + "pattern": "Debian.* 12", + "endOfLife": "2028-06-30" + }, + { + "pattern": "Fedora.* 36", + "endOfLife": "2023-05-18" + }, + { + "pattern": "Fedora.* 37", + "endOfLife": "2023-12-15" + }, + { + "pattern": "Fedora.* 38", + "endOfLife": "2023-05-18" + }, + { + "pattern": "Oracle Linux.* 7", + "start": "2023-05-01", + "endOfLife": "2023-11-16" + }, + { + "pattern": "Oracle Linux.* 8", + "endOfLife": "2029-03-31" + }, + { + "pattern": "Red Hat Enterprise Linux.* 7", + "start": "2023-05-01", + "endOfLife": "2023-11-16" + }, + { + "pattern": "Red Hat Enterprise Linux.* 8", + "endOfLife": "2029-03-31" + }, + { + "pattern": "Rocky Linux.* 8", + "endOfLife": "2029-03-31" + }, + { + "pattern": "Scientific Linux.* 7", + "start": "2023-05-01", + "endOfLife": "2023-11-16" + }, + { + "pattern": "Ubuntu.* 18.04", + "endOfLife": "2023-05-31" + }, + { + "pattern": "Ubuntu.* 20.04", + "endOfLife": "2025-04-02" + }, + { + "pattern": "Ubuntu.* 22.04", + "endOfLife": "2027-04-01" + } +] diff --git a/core/src/main/resources/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor/message.jelly b/core/src/main/resources/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor/message.jelly new file mode 100644 index 0000000000..e61f7a4e88 --- /dev/null +++ b/core/src/main/resources/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor/message.jelly @@ -0,0 +1,45 @@ + + + + +
+
+ + + + + +

${it.displayName}

+ + + ${%Past_End_of_Life(it.operatingSystemName, it.endOfLifeDate, it.documentationUrl)} + + + ${%Upcoming_End_of_Life(it.operatingSystemName, it.endOfLifeDate, it.documentationUrl)} + + +
+
diff --git a/core/src/main/resources/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor/message.properties b/core/src/main/resources/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor/message.properties new file mode 100644 index 0000000000..fcc27beff2 --- /dev/null +++ b/core/src/main/resources/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor/message.properties @@ -0,0 +1,9 @@ +End_of_Life_Heading={0} in Jenkins +Upcoming_End_of_Life=

You are running Jenkins on {0}. \ + Jenkins will no longer support {0} after {1}.\ +

Please plan your upgrade to a supported operating system before {1}.

\ +

Refer to the documentation for details.

+Past_End_of_Life=

You are running Jenkins on {0}. \ + Jenkins stopped supporting {0} as of {1}.\ +

Please upgrade to a supported operating system.

\ +

Refer to the documentation for details.

diff --git a/core/src/test/java/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitorTest.java b/core/src/test/java/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitorTest.java new file mode 100644 index 0000000000..7125896597 --- /dev/null +++ b/core/src/test/java/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitorTest.java @@ -0,0 +1,334 @@ +/* + * The MIT License + * + * Copyright 2023 Mark Waite. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package jenkins.monitor; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; + +import java.io.File; +import java.io.IOException; +import java.util.Random; +import org.junit.Test; + +public class OperatingSystemEndOfLifeAdminMonitorTest { + + private final OperatingSystemEndOfLifeAdminMonitor monitor; + private final Random random = new Random(); + private final String PREFIX = "administrativeMonitor/"; + + public OperatingSystemEndOfLifeAdminMonitorTest() throws IOException { + this.monitor = random.nextBoolean() + ? new OperatingSystemEndOfLifeAdminMonitor() + : new OperatingSystemEndOfLifeAdminMonitor(OperatingSystemEndOfLifeAdminMonitor.class.getName()); + } + + @Test + public void testGetDisplayName() { + assertThat(monitor.getDisplayName(), is("Operating system end of life monitor")); + } + + @Test + public void testGetAfterEndOfLifeDate() { + assertFalse(monitor.getAfterEndOfLifeDate()); + } + + @Test + public void testGetDocumentationUrl() { + assertThat(monitor.getDocumentationUrl(), is(not(nullValue()))); + } + + @Test + public void testGetEndOfLifeDate() { + assertThat(monitor.getEndOfLifeDate(), is("2099-12-31")); + } + + @Test + public void testGetOperatingSystemName() { + /* Operating system name depends on the operating system running test */ + assertThat(monitor.getOperatingSystemName(), not(nullValue())); + } + + @Test + public void testGetSearchUrl() { + assertThat(monitor.getSearchUrl(), is(PREFIX + monitor.getClass().getName())); + } + + @Test + public void testGetUrl() { + assertThat(monitor.getUrl(), is(PREFIX + monitor.getClass().getName())); + } + + @Test + public void testIsActivated() throws IOException { + // Will fail if operating system running the test is reaching end of life soon + assertFalse(monitor.isActivated()); + } + + @Test + public void testIsSecurity() throws IOException { + assertFalse(monitor.isSecurity()); + } + + @Test + public void testNotIsActivatedWhenIgnoreEndOfLife() throws IOException { + monitor.ignoreEndOfLife = true; + assertFalse(monitor.isActivated()); + } + + private String docsUrl(String component) { + return "https://www.jenkins.io/redirect/operating-system-end-of-life?q=" + component; + } + + @Test + public void testReadDocumentationUrlAlma8() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-alma-8").toURI()); + assertThat(monitor.readDocumentationUrl(releaseFile, "AlmaLinux.* 8"), is(docsUrl("AlmaLinux-8.7-Stone-Smilodon"))); + } + + @Test + public void testReadDocumentationUrlAlpine314() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-alpine-3.14").toURI()); + assertThat(monitor.readDocumentationUrl(releaseFile, "Alpine Linux v3.14"), is(docsUrl("Alpine-Linux-v3.14"))); + } + + @Test + public void testReadDocumentationUrlAlpine315() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-alpine-3.15").toURI()); + assertThat(monitor.readDocumentationUrl(releaseFile, "Alpine Linux v3.15"), is(docsUrl("Alpine-Linux-v3.15"))); + } + + @Test + public void testReadDocumentationUrlAlpine316() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-alpine-3.16").toURI()); + assertThat(monitor.readDocumentationUrl(releaseFile, "Alpine Linux v3.16"), is(docsUrl("Alpine-Linux-v3.16"))); + } + + @Test + public void testReadDocumentationUrlAlpine317() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-alpine-3.17").toURI()); + assertThat(monitor.readDocumentationUrl(releaseFile, "Alpine Linux v3.17"), is(docsUrl("Alpine-Linux-v3.17"))); + } + + @Test + public void testReadDocumentationUrlAlpine318() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-alpine-3.18").toURI()); + assertThat(monitor.readDocumentationUrl(releaseFile, "Alpine Linux v3.18"), is(docsUrl("Alpine-Linux-v3.18"))); + } + + @Test + public void testReadDocumentationUrlCentOS7() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-centos-7").toURI()); + assertThat(monitor.readDocumentationUrl(releaseFile, "CentOS Linux.* 7"), is(docsUrl("CentOS-Linux-7-Core"))); + } + + @Test + public void testReadDocumentationUrlDebian10() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-debian-10").toURI()); + assertThat(monitor.readDocumentationUrl(releaseFile, "Debian.* 10"), is(docsUrl("Debian-GNU-Linux-10-buster"))); + } + + @Test + public void testReadDocumentationUrlFedora36() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-fedora-36").toURI()); + assertThat(monitor.readDocumentationUrl(releaseFile, "Fedora.* 36"), is(docsUrl("Fedora-Linux-36-Container-Image"))); + } + + @Test + public void testReadDocumentationUrlFedora37() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-fedora-37").toURI()); + assertThat(monitor.readDocumentationUrl(releaseFile, "Fedora.* 37"), is(docsUrl("Fedora-Linux-37-Container-Image"))); + } + + @Test + public void testReadDocumentationUrlOracle7() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-oracle-7").toURI()); + assertThat(monitor.readDocumentationUrl(releaseFile, "Oracle Linux.* 7"), is(docsUrl("Oracle-Linux-Server-7.9"))); + } + + @Test + public void testReadDocumentationUrlOracle8() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-oracle-8").toURI()); + assertThat(monitor.readDocumentationUrl(releaseFile, "Oracle Linux.* 8"), is(docsUrl("Oracle-Linux-Server-8.7"))); + } + + @Test + public void testReadDocumentationUrlRedHat7() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-redhat-7").toURI()); + assertThat(monitor.readDocumentationUrl(releaseFile, "Red Hat Enterprise Linux.* 7"), is(docsUrl("Red-Hat-Enterprise-Linux-Server-7.9-Maipo"))); + } + + @Test + public void testReadDocumentationUrlRedHat8() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-redhat-8").toURI()); + assertThat(monitor.readDocumentationUrl(releaseFile, "Red Hat Enterprise Linux.* 8"), is(docsUrl("Red-Hat-Enterprise-Linux-8.8-Ootpa"))); + } + + @Test + public void testReadDocumentationUrlRocky8() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-rocky-8").toURI()); + assertThat(monitor.readDocumentationUrl(releaseFile, "Rocky Linux.* 8"), is(docsUrl("Rocky-Linux-8.7-Green-Obsidian"))); + } + + @Test + public void testReadDocumentationUrlScientific7() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-scientific-7").toURI()); + assertThat(monitor.readDocumentationUrl(releaseFile, "Scientific Linux.* 7"), is(docsUrl("Scientific-Linux-7.9-Nitrogen"))); + } + + @Test + public void testReadDocumentationUrlUbuntu18() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-ubuntu-18.04").toURI()); + assertThat(monitor.readDocumentationUrl(releaseFile, "Ubuntu.* 18"), is(docsUrl("Ubuntu-18.04.6-LTS"))); + } + + @Test + public void testReadOperatingSystemListEmptySet() { + IOException e = assertThrows(IOException.class, () -> monitor.readOperatingSystemList("[]")); + assertThat(e.getMessage(), is("Empty data set")); + } + + @Test + public void testReadOperatingSystemListNoEndOfLife() { + IOException e = assertThrows(IOException.class, () -> monitor.readOperatingSystemList("[{\"pattern\": \"Alpine\"}]")); + assertThat(e.getMessage(), is("No end of life date for Alpine")); + } + + @Test + public void testReadOperatingSystemListNoPattern() { + IOException e = assertThrows(IOException.class, () -> monitor.readOperatingSystemList("[{\"endOfLife\": \"2029-03-31\"}]")); + assertThat(e.getMessage(), is("Missing pattern in definition file")); + } + + @Test + public void testReadOperatingSystemNameAlma8() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-alma-8").toURI()); + assertThat(monitor.readOperatingSystemName(releaseFile, "AlmaLinux.* 8"), is("AlmaLinux 8.7 (Stone Smilodon)")); + } + + @Test + public void testReadOperatingSystemNameAlpine314() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-alpine-3.14").toURI()); + assertThat(monitor.readOperatingSystemName(releaseFile, "Alpine Linux v3.14"), is("Alpine Linux v3.14")); + } + + @Test + public void testReadOperatingSystemNameAlpine315() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-alpine-3.15").toURI()); + assertThat(monitor.readOperatingSystemName(releaseFile, "Alpine Linux v3.15"), is("Alpine Linux v3.15")); + } + + @Test + public void testReadOperatingSystemNameAlpine316() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-alpine-3.16").toURI()); + assertThat(monitor.readOperatingSystemName(releaseFile, "Alpine Linux v3.16"), is("Alpine Linux v3.16")); + } + + @Test + public void testReadOperatingSystemNameAlpine317() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-alpine-3.17").toURI()); + assertThat(monitor.readOperatingSystemName(releaseFile, "Alpine Linux v3.17"), is("Alpine Linux v3.17")); + } + + @Test + public void testReadOperatingSystemNameAlpine318() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-alpine-3.18").toURI()); + assertThat(monitor.readOperatingSystemName(releaseFile, "Alpine Linux v3.18"), is("Alpine Linux v3.18")); + } + + @Test + public void testReadOperatingSystemNameCentOS7() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-centos-7").toURI()); + assertThat(monitor.readOperatingSystemName(releaseFile, "CentOS Linux.* 7"), is("CentOS Linux 7 (Core)")); + } + + @Test + public void testReadOperatingSystemNameDebian10() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-debian-10").toURI()); + assertThat(monitor.readOperatingSystemName(releaseFile, "Debian.* 10"), is("Debian GNU/Linux 10 (buster)")); + } + + @Test + public void testReadOperatingSystemNameFedora36() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-fedora-36").toURI()); + assertThat(monitor.readOperatingSystemName(releaseFile, "Fedora.* 36"), is("Fedora Linux 36 (Container Image)")); + } + + @Test + public void testReadOperatingSystemNameFedora37() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-fedora-37").toURI()); + assertThat(monitor.readOperatingSystemName(releaseFile, "Fedora.* 37"), is("Fedora Linux 37 (Container Image)")); + } + + @Test + public void testReadOperatingSystemNameMissingFile() { + assertThat(monitor.readOperatingSystemName(new File("/this/file/does/not/exist"), ".*"), is("")); + } + + @Test + public void testReadOperatingSystemNameOracle7() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-oracle-7").toURI()); + assertThat(monitor.readOperatingSystemName(releaseFile, "Oracle Linux.* 7"), is("Oracle Linux Server 7.9")); + } + + @Test + public void testReadOperatingSystemNameOracle8() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-oracle-8").toURI()); + assertThat(monitor.readOperatingSystemName(releaseFile, "Oracle Linux.* 8"), is("Oracle Linux Server 8.7")); + } + + @Test + public void testReadOperatingSystemNameRedHat7() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-redhat-7").toURI()); + assertThat(monitor.readOperatingSystemName(releaseFile, "Red Hat Enterprise Linux.* 7"), is("Red Hat Enterprise Linux Server 7.9 (Maipo)")); + } + + @Test + public void testReadOperatingSystemNameRedHat8() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-redhat-8").toURI()); + assertThat(monitor.readOperatingSystemName(releaseFile, "Red Hat Enterprise Linux.* 8"), is("Red Hat Enterprise Linux 8.8 (Ootpa)")); + } + + @Test + public void testReadOperatingSystemNameRocky8() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-rocky-8").toURI()); + assertThat(monitor.readOperatingSystemName(releaseFile, "Rocky Linux.* 8"), is("Rocky Linux 8.7 (Green Obsidian)")); + } + + @Test + public void testReadOperatingSystemNameScientific7() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-scientific-7").toURI()); + assertThat(monitor.readOperatingSystemName(releaseFile, "Scientific Linux.* 7"), is("Scientific Linux 7.9 (Nitrogen)")); + } + + @Test + public void testReadOperatingSystemNameUbuntu18() throws Exception { + File releaseFile = new File(this.getClass().getResource("os-release-ubuntu-18.04").toURI()); + assertThat(monitor.readOperatingSystemName(releaseFile, "Ubuntu.* 18"), is("Ubuntu 18.04.6 LTS")); + } +} diff --git a/core/src/test/resources/jenkins/monitor/os-release-alma-8 b/core/src/test/resources/jenkins/monitor/os-release-alma-8 new file mode 100644 index 0000000000..d483909db5 --- /dev/null +++ b/core/src/test/resources/jenkins/monitor/os-release-alma-8 @@ -0,0 +1,18 @@ +NAME="AlmaLinux" +VERSION="8.7 (Stone Smilodon)" +ID="almalinux" +ID_LIKE="rhel centos fedora" +VERSION_ID="8.7" +PLATFORM_ID="platform:el8" +PRETTY_NAME="AlmaLinux 8.7 (Stone Smilodon)" +ANSI_COLOR="0;34" +LOGO="fedora-logo-icon" +CPE_NAME="cpe:/o:almalinux:almalinux:8::baseos" +HOME_URL="https://almalinux.org/" +DOCUMENTATION_URL="https://wiki.almalinux.org/" +BUG_REPORT_URL="https://bugs.almalinux.org/" + +ALMALINUX_MANTISBT_PROJECT="AlmaLinux-8" +ALMALINUX_MANTISBT_PROJECT_VERSION="8.7" +REDHAT_SUPPORT_PRODUCT="AlmaLinux" +REDHAT_SUPPORT_PRODUCT_VERSION="8.7" diff --git a/core/src/test/resources/jenkins/monitor/os-release-alpine-3.14 b/core/src/test/resources/jenkins/monitor/os-release-alpine-3.14 new file mode 100644 index 0000000000..55adf73bc9 --- /dev/null +++ b/core/src/test/resources/jenkins/monitor/os-release-alpine-3.14 @@ -0,0 +1,6 @@ +NAME="Alpine Linux" +ID=alpine +VERSION_ID=3.14.10 +PRETTY_NAME="Alpine Linux v3.14" +HOME_URL="https://alpinelinux.org/" +BUG_REPORT_URL="https://bugs.alpinelinux.org/" diff --git a/core/src/test/resources/jenkins/monitor/os-release-alpine-3.15 b/core/src/test/resources/jenkins/monitor/os-release-alpine-3.15 new file mode 100644 index 0000000000..8857f30893 --- /dev/null +++ b/core/src/test/resources/jenkins/monitor/os-release-alpine-3.15 @@ -0,0 +1,6 @@ +NAME="Alpine Linux" +ID=alpine +VERSION_ID=3.15.8 +PRETTY_NAME="Alpine Linux v3.15" +HOME_URL="https://alpinelinux.org/" +BUG_REPORT_URL="https://bugs.alpinelinux.org/" diff --git a/core/src/test/resources/jenkins/monitor/os-release-alpine-3.16 b/core/src/test/resources/jenkins/monitor/os-release-alpine-3.16 new file mode 100644 index 0000000000..b523a229bf --- /dev/null +++ b/core/src/test/resources/jenkins/monitor/os-release-alpine-3.16 @@ -0,0 +1,6 @@ +NAME="Alpine Linux" +ID=alpine +VERSION_ID=3.16.5 +PRETTY_NAME="Alpine Linux v3.16" +HOME_URL="https://alpinelinux.org/" +BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues" diff --git a/core/src/test/resources/jenkins/monitor/os-release-alpine-3.17 b/core/src/test/resources/jenkins/monitor/os-release-alpine-3.17 new file mode 100644 index 0000000000..8478d8e8bf --- /dev/null +++ b/core/src/test/resources/jenkins/monitor/os-release-alpine-3.17 @@ -0,0 +1,6 @@ +NAME="Alpine Linux" +ID=alpine +VERSION_ID=3.17.3 +PRETTY_NAME="Alpine Linux v3.17" +HOME_URL="https://alpinelinux.org/" +BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues" diff --git a/core/src/test/resources/jenkins/monitor/os-release-alpine-3.18 b/core/src/test/resources/jenkins/monitor/os-release-alpine-3.18 new file mode 100644 index 0000000000..a9e1d0057d --- /dev/null +++ b/core/src/test/resources/jenkins/monitor/os-release-alpine-3.18 @@ -0,0 +1,6 @@ +NAME="Alpine Linux" +ID=alpine +VERSION_ID=3.18.0 +PRETTY_NAME="Alpine Linux v3.18" +HOME_URL="https://alpinelinux.org/" +BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues" diff --git a/core/src/test/resources/jenkins/monitor/os-release-centos-7 b/core/src/test/resources/jenkins/monitor/os-release-centos-7 new file mode 100644 index 0000000000..7037a940fa --- /dev/null +++ b/core/src/test/resources/jenkins/monitor/os-release-centos-7 @@ -0,0 +1,16 @@ +NAME="CentOS Linux" +VERSION="7 (Core)" +ID="centos" +ID_LIKE="rhel fedora" +VERSION_ID="7" +PRETTY_NAME="CentOS Linux 7 (Core)" +ANSI_COLOR="0;31" +CPE_NAME="cpe:/o:centos:centos:7" +HOME_URL="https://www.centos.org/" +BUG_REPORT_URL="https://bugs.centos.org/" + +CENTOS_MANTISBT_PROJECT="CentOS-7" +CENTOS_MANTISBT_PROJECT_VERSION="7" +REDHAT_SUPPORT_PRODUCT="centos" +REDHAT_SUPPORT_PRODUCT_VERSION="7" + diff --git a/core/src/test/resources/jenkins/monitor/os-release-debian-10 b/core/src/test/resources/jenkins/monitor/os-release-debian-10 new file mode 100644 index 0000000000..9b5419df8b --- /dev/null +++ b/core/src/test/resources/jenkins/monitor/os-release-debian-10 @@ -0,0 +1,9 @@ +PRETTY_NAME="Debian GNU/Linux 10 (buster)" +NAME="Debian GNU/Linux" +VERSION_ID="10" +VERSION="10 (buster)" +VERSION_CODENAME=buster +ID=debian +HOME_URL="https://www.debian.org/" +SUPPORT_URL="https://www.debian.org/support" +BUG_REPORT_URL="https://bugs.debian.org/" diff --git a/core/src/test/resources/jenkins/monitor/os-release-fedora-36 b/core/src/test/resources/jenkins/monitor/os-release-fedora-36 new file mode 100644 index 0000000000..ae039188b2 --- /dev/null +++ b/core/src/test/resources/jenkins/monitor/os-release-fedora-36 @@ -0,0 +1,22 @@ +NAME="Fedora Linux" +VERSION="36 (Container Image)" +ID=fedora +VERSION_ID=36 +VERSION_CODENAME="" +PLATFORM_ID="platform:f36" +PRETTY_NAME="Fedora Linux 36 (Container Image)" +ANSI_COLOR="0;38;2;60;110;180" +LOGO=fedora-logo-icon +CPE_NAME="cpe:/o:fedoraproject:fedora:36" +HOME_URL="https://fedoraproject.org/" +DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f36/system-administrators-guide/" +SUPPORT_URL="https://ask.fedoraproject.org/" +BUG_REPORT_URL="https://bugzilla.redhat.com/" +REDHAT_BUGZILLA_PRODUCT="Fedora" +REDHAT_BUGZILLA_PRODUCT_VERSION=36 +REDHAT_SUPPORT_PRODUCT="Fedora" +REDHAT_SUPPORT_PRODUCT_VERSION=36 +PRIVACY_POLICY_URL="https://fedoraproject.org/wiki/Legal:PrivacyPolicy" +SUPPORT_END=2023-05-16 +VARIANT="Container Image" +VARIANT_ID=container diff --git a/core/src/test/resources/jenkins/monitor/os-release-fedora-37 b/core/src/test/resources/jenkins/monitor/os-release-fedora-37 new file mode 100644 index 0000000000..a44f5bc6ef --- /dev/null +++ b/core/src/test/resources/jenkins/monitor/os-release-fedora-37 @@ -0,0 +1,22 @@ +NAME="Fedora Linux" +VERSION="37 (Container Image)" +ID=fedora +VERSION_ID=37 +VERSION_CODENAME="" +PLATFORM_ID="platform:f37" +PRETTY_NAME="Fedora Linux 37 (Container Image)" +ANSI_COLOR="0;38;2;60;110;180" +LOGO=fedora-logo-icon +CPE_NAME="cpe:/o:fedoraproject:fedora:37" +DEFAULT_HOSTNAME="fedora" +HOME_URL="https://fedoraproject.org/" +DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f37/system-administrators-guide/" +SUPPORT_URL="https://ask.fedoraproject.org/" +BUG_REPORT_URL="https://bugzilla.redhat.com/" +REDHAT_BUGZILLA_PRODUCT="Fedora" +REDHAT_BUGZILLA_PRODUCT_VERSION=37 +REDHAT_SUPPORT_PRODUCT="Fedora" +REDHAT_SUPPORT_PRODUCT_VERSION=37 +SUPPORT_END=2023-11-14 +VARIANT="Container Image" +VARIANT_ID=container diff --git a/core/src/test/resources/jenkins/monitor/os-release-oracle-7 b/core/src/test/resources/jenkins/monitor/os-release-oracle-7 new file mode 100644 index 0000000000..f8823a8e1c --- /dev/null +++ b/core/src/test/resources/jenkins/monitor/os-release-oracle-7 @@ -0,0 +1,17 @@ +NAME="Oracle Linux Server" +VERSION="7.9" +ID="ol" +ID_LIKE="fedora" +VARIANT="Server" +VARIANT_ID="server" +VERSION_ID="7.9" +PRETTY_NAME="Oracle Linux Server 7.9" +ANSI_COLOR="0;31" +CPE_NAME="cpe:/o:oracle:linux:7:9:server" +HOME_URL="https://linux.oracle.com/" +BUG_REPORT_URL="https://bugzilla.oracle.com/" + +ORACLE_BUGZILLA_PRODUCT="Oracle Linux 7" +ORACLE_BUGZILLA_PRODUCT_VERSION=7.9 +ORACLE_SUPPORT_PRODUCT="Oracle Linux" +ORACLE_SUPPORT_PRODUCT_VERSION=7.9 diff --git a/core/src/test/resources/jenkins/monitor/os-release-oracle-8 b/core/src/test/resources/jenkins/monitor/os-release-oracle-8 new file mode 100644 index 0000000000..b91c366e47 --- /dev/null +++ b/core/src/test/resources/jenkins/monitor/os-release-oracle-8 @@ -0,0 +1,18 @@ +NAME="Oracle Linux Server" +VERSION="8.7" +ID="ol" +ID_LIKE="fedora" +VARIANT="Server" +VARIANT_ID="server" +VERSION_ID="8.7" +PLATFORM_ID="platform:el8" +PRETTY_NAME="Oracle Linux Server 8.7" +ANSI_COLOR="0;31" +CPE_NAME="cpe:/o:oracle:linux:8:7:server" +HOME_URL="https://linux.oracle.com/" +BUG_REPORT_URL="https://bugzilla.oracle.com/" + +ORACLE_BUGZILLA_PRODUCT="Oracle Linux 8" +ORACLE_BUGZILLA_PRODUCT_VERSION=8.7 +ORACLE_SUPPORT_PRODUCT="Oracle Linux" +ORACLE_SUPPORT_PRODUCT_VERSION=8.7 diff --git a/core/src/test/resources/jenkins/monitor/os-release-redhat-7 b/core/src/test/resources/jenkins/monitor/os-release-redhat-7 new file mode 100644 index 0000000000..ec038b12aa --- /dev/null +++ b/core/src/test/resources/jenkins/monitor/os-release-redhat-7 @@ -0,0 +1,17 @@ +NAME="Red Hat Enterprise Linux Server" +VERSION="7.9 (Maipo)" +ID="rhel" +ID_LIKE="fedora" +VARIANT="Server" +VARIANT_ID="server" +VERSION_ID="7.9" +PRETTY_NAME="Red Hat Enterprise Linux Server 7.9 (Maipo)" +ANSI_COLOR="0;31" +CPE_NAME="cpe:/o:redhat:enterprise_linux:7.9:GA:server" +HOME_URL="https://www.redhat.com/" +BUG_REPORT_URL="https://bugzilla.redhat.com/" + +REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 7" +REDHAT_BUGZILLA_PRODUCT_VERSION=7.9 +REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux" +REDHAT_SUPPORT_PRODUCT_VERSION="7.9" diff --git a/core/src/test/resources/jenkins/monitor/os-release-redhat-8 b/core/src/test/resources/jenkins/monitor/os-release-redhat-8 new file mode 100644 index 0000000000..2fafad589d --- /dev/null +++ b/core/src/test/resources/jenkins/monitor/os-release-redhat-8 @@ -0,0 +1,17 @@ +NAME="Red Hat Enterprise Linux" +VERSION="8.8 (Ootpa)" +ID="rhel" +ID_LIKE="fedora" +VERSION_ID="8.8" +PLATFORM_ID="platform:el8" +PRETTY_NAME="Red Hat Enterprise Linux 8.8 (Ootpa)" +ANSI_COLOR="0;31" +CPE_NAME="cpe:/o:redhat:enterprise_linux:8::baseos" +HOME_URL="https://www.redhat.com/" +DOCUMENTATION_URL="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8" +BUG_REPORT_URL="https://bugzilla.redhat.com/" + +REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 8" +REDHAT_BUGZILLA_PRODUCT_VERSION=8.8 +REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux" +REDHAT_SUPPORT_PRODUCT_VERSION="8.8" diff --git a/core/src/test/resources/jenkins/monitor/os-release-rocky-8 b/core/src/test/resources/jenkins/monitor/os-release-rocky-8 new file mode 100644 index 0000000000..2fd5d0f4bc --- /dev/null +++ b/core/src/test/resources/jenkins/monitor/os-release-rocky-8 @@ -0,0 +1,16 @@ +NAME="Rocky Linux" +VERSION="8.7 (Green Obsidian)" +ID="rocky" +ID_LIKE="rhel centos fedora" +VERSION_ID="8.7" +PLATFORM_ID="platform:el8" +PRETTY_NAME="Rocky Linux 8.7 (Green Obsidian)" +ANSI_COLOR="0;32" +LOGO="fedora-logo-icon" +CPE_NAME="cpe:/o:rocky:rocky:8:GA" +HOME_URL="https://rockylinux.org/" +BUG_REPORT_URL="https://bugs.rockylinux.org/" +ROCKY_SUPPORT_PRODUCT="Rocky-Linux-8" +ROCKY_SUPPORT_PRODUCT_VERSION="8.7" +REDHAT_SUPPORT_PRODUCT="Rocky Linux" +REDHAT_SUPPORT_PRODUCT_VERSION="8.7" diff --git a/core/src/test/resources/jenkins/monitor/os-release-scientific-7 b/core/src/test/resources/jenkins/monitor/os-release-scientific-7 new file mode 100644 index 0000000000..e8ba770fd4 --- /dev/null +++ b/core/src/test/resources/jenkins/monitor/os-release-scientific-7 @@ -0,0 +1,15 @@ +NAME="Scientific Linux" +VERSION="7.9 (Nitrogen)" +ID="scientific" +ID_LIKE="rhel centos fedora" +VERSION_ID="7.9" +PRETTY_NAME="Scientific Linux 7.9 (Nitrogen)" +ANSI_COLOR="0;31" +CPE_NAME="cpe:/o:scientificlinux:scientificlinux:7.9:GA" +HOME_URL="http://www.scientificlinux.org//" +BUG_REPORT_URL="mailto:scientific-linux-devel@listserv.fnal.gov" + +REDHAT_BUGZILLA_PRODUCT="Scientific Linux 7" +REDHAT_BUGZILLA_PRODUCT_VERSION=7.9 +REDHAT_SUPPORT_PRODUCT="Scientific Linux" +REDHAT_SUPPORT_PRODUCT_VERSION="7.9" diff --git a/core/src/test/resources/jenkins/monitor/os-release-ubuntu-18.04 b/core/src/test/resources/jenkins/monitor/os-release-ubuntu-18.04 new file mode 100644 index 0000000000..097c592002 --- /dev/null +++ b/core/src/test/resources/jenkins/monitor/os-release-ubuntu-18.04 @@ -0,0 +1,12 @@ +NAME="Ubuntu" +VERSION="18.04.6 LTS (Bionic Beaver)" +ID=ubuntu +ID_LIKE=debian +PRETTY_NAME="Ubuntu 18.04.6 LTS" +VERSION_ID="18.04" +HOME_URL="https://www.ubuntu.com/" +SUPPORT_URL="https://help.ubuntu.com/" +BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" +PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" +VERSION_CODENAME=bionic +UBUNTU_CODENAME=bionic