From 4943f167de2b2a15171f7a655c58f56c6e7ca320 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Thu, 12 Dec 2024 16:40:26 -0500 Subject: [PATCH] Recover views after error in `Jenkins.load` (#10023) * Recover views after error in `Jenkins.load` * Prevent infinite recursion in `Jenkins/sidepanel.jelly` when `Jenkins.primaryView` is null --------- Co-authored-by: Tim Jacomb <21194782+timja@users.noreply.github.com> --- core/src/main/java/jenkins/model/Jenkins.java | 14 +++++++++++--- .../jenkins/model/Jenkins/sidepanel.jelly | 7 ++++++- .../src/test/java/jenkins/model/JenkinsTest.java | 16 ++++++++++++++++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index eee96b5bca05..e643e848dd9b 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -3297,11 +3297,19 @@ public void load() throws IOException { if (cfg.exists()) { // reset some data that may not exist in the disk file // so that we can take a proper compensation action later. + String originalPrimaryView = primaryView; + List originalViews = new ArrayList<>(views); primaryView = null; views.clear(); - - // load from disk - cfg.unmarshal(Jenkins.this); + try { + // load from disk + cfg.unmarshal(Jenkins.this); + } catch (IOException | RuntimeException x) { + primaryView = originalPrimaryView; + views.clear(); + views.addAll(originalViews); + throw x; + } } // initialize views by inserting the default view if necessary // this is both for clean Jenkins and for backward compatibility. diff --git a/core/src/main/resources/jenkins/model/Jenkins/sidepanel.jelly b/core/src/main/resources/jenkins/model/Jenkins/sidepanel.jelly index 9ddff73647d6..e413712d6e7c 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/sidepanel.jelly +++ b/core/src/main/resources/jenkins/model/Jenkins/sidepanel.jelly @@ -23,4 +23,9 @@ THE SOFTWARE. --> - \ No newline at end of file + + + + + + diff --git a/test/src/test/java/jenkins/model/JenkinsTest.java b/test/src/test/java/jenkins/model/JenkinsTest.java index 8469c9b46c36..acef3b6a2300 100644 --- a/test/src/test/java/jenkins/model/JenkinsTest.java +++ b/test/src/test/java/jenkins/model/JenkinsTest.java @@ -30,9 +30,11 @@ import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.arrayContaining; +import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isA; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -46,6 +48,7 @@ import hudson.XmlFile; import hudson.init.InitMilestone; import hudson.init.Initializer; +import hudson.model.AllView; import hudson.model.Computer; import hudson.model.Failure; import hudson.model.FreeStyleProject; @@ -101,6 +104,7 @@ import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.rules.TemporaryFolder; +import org.jvnet.hudson.reactor.ReactorException; import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.JenkinsRule.WebClient; @@ -755,4 +759,16 @@ public String getUrlName() { return null; } } + + @Test + public void reloadViews() throws Exception { + assertThat(j.jenkins.getPrimaryView(), isA(AllView.class)); + assertThat(j.jenkins.getViews(), contains(isA(AllView.class))); + Files.writeString(j.jenkins.getConfigFile().getFile().toPath(), "