diff --git a/core/src/main/java/hudson/util/RemotingDiagnostics.java b/core/src/main/java/hudson/util/RemotingDiagnostics.java index ba383e2081551bf455f0e3eec60c8d90a0b1cf68..8a2a51aa28608fc251652dbd90ef4f7715a26b16 100644 --- a/core/src/main/java/hudson/util/RemotingDiagnostics.java +++ b/core/src/main/java/hudson/util/RemotingDiagnostics.java @@ -34,15 +34,18 @@ import hudson.remoting.Future; import hudson.remoting.VirtualChannel; import hudson.security.AccessControlled; import jenkins.security.MasterToSlaveCallable; + import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.customizers.ImportCustomizer; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.WebMethod; +import javax.annotation.Nonnull; import javax.management.JMException; import javax.management.MBeanServer; import javax.management.ObjectName; + import java.io.File; import java.io.IOException; import java.io.PrintWriter; @@ -104,7 +107,7 @@ public final class RemotingDiagnostics { /** * Executes Groovy script remotely. */ - public static String executeGroovy(String script, VirtualChannel channel) throws IOException, InterruptedException { + public static String executeGroovy(String script, @Nonnull VirtualChannel channel) throws IOException, InterruptedException { return channel.call(new Script(script)); } diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 8694f7a1f7bb22e84cc4c6524587b526d46a684a..8b024f018eab55ebef8abdb46dcf7e3998bf36ab 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -3527,6 +3527,11 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve if (!"POST".equals(req.getMethod())) { throw HttpResponses.error(HttpURLConnection.HTTP_BAD_METHOD, "requires POST"); } + + if (channel == null) { + throw HttpResponses.error(HttpURLConnection.HTTP_NOT_FOUND, "Node is offline"); + } + try { req.setAttribute("output", RemotingDiagnostics.executeGroovy(text, channel)); diff --git a/core/src/main/resources/hudson/model/Computer/sidepanel.jelly b/core/src/main/resources/hudson/model/Computer/sidepanel.jelly index 7d81d64346332571927305f64a676785c731ceb2..2a74c154e76ab2050b44ae38808e9a37c0a71610 100644 --- a/core/src/main/resources/hudson/model/Computer/sidepanel.jelly +++ b/core/src/main/resources/hudson/model/Computer/sidepanel.jelly @@ -36,7 +36,9 @@ THE SOFTWARE. - + + + @@ -45,4 +47,4 @@ THE SOFTWARE. - \ No newline at end of file + diff --git a/core/src/main/resources/lib/hudson/scriptConsole.jelly b/core/src/main/resources/lib/hudson/scriptConsole.jelly index 205ea555e96a0a1c8e0fb4d7080596e1e0d602dd..bf2195db5b53b79b0867dd2b9a108f1e96133235 100644 --- a/core/src/main/resources/lib/hudson/scriptConsole.jelly +++ b/core/src/main/resources/lib/hudson/scriptConsole.jelly @@ -31,29 +31,36 @@ THE SOFTWARE. -

${%Script Console}

- -

- ${%description} -

- - -

- ${%description2} -

- -
- -
- -
-
- - - -

${%Result}

-
-
+

${%Script Console}

+ + + +

+ ${%description} +

+ + +

+ ${%description2} +

+ +
+ +
+ +
+
+ + + +

${%Result}

+
+
+
+ + ${%It is not possible to run scripts when slave is offline.} + +
- \ 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 18d42ad4b11127dff1245911099bded66de4972c..4b4bc7422d655efe635e155b95659dc0fe39361c 100644 --- a/test/src/test/java/jenkins/model/JenkinsTest.java +++ b/test/src/test/java/jenkins/model/JenkinsTest.java @@ -23,6 +23,9 @@ */ package jenkins.model; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertEquals; @@ -31,7 +34,9 @@ import static org.junit.Assert.fail; import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; import com.gargoylesoftware.htmlunit.HttpMethod; +import com.gargoylesoftware.htmlunit.Page; import com.gargoylesoftware.htmlunit.WebRequestSettings; +import com.gargoylesoftware.htmlunit.WebResponse; import com.gargoylesoftware.htmlunit.html.HtmlForm; import com.gargoylesoftware.htmlunit.html.HtmlPage; @@ -50,6 +55,7 @@ import hudson.security.GlobalMatrixAuthorizationStrategy; import hudson.security.LegacySecurityRealm; import hudson.security.Permission; import hudson.slaves.ComputerListener; +import hudson.slaves.DumbSlave; import hudson.slaves.OfflineCause; import hudson.util.FormValidation; @@ -427,4 +433,20 @@ public class JenkinsTest { @TestExtension(value = "testComputerListenerNotifiedOnRestart") public static final ComputerListener listenerMock = Mockito.mock(ComputerListener.class); + + @Test + public void runScriptOnOfflineComputer() throws Exception { + DumbSlave slave = j.createSlave(); + URL url = new URL(j.getURL(), "computer/" + slave.getNodeName() + "/scriptText?script=println(42)"); + + WebClient wc = j.createWebClient(); + wc.setThrowExceptionOnFailingStatusCode(false); + + WebRequestSettings req = new WebRequestSettings(url, HttpMethod.POST); + Page page = wc.getPage(wc.addCrumb(req)); + WebResponse rsp = page.getWebResponse(); + + assertThat(rsp.getContentAsString(), containsString("Node is offline")); + assertThat(rsp.getStatusCode(), equalTo(404)); + } }