Should only be called when at least one byte is available. 47 | * Whatever bytes are actually read will not be offered on the next call, if there is one; there is no need to close the stream. 48 | *
There is no guarantee that output is offered in the form of complete lines of text, 49 | * though in the typical case of line-oriented output it is likely that it will end in a newline. 50 | *
Buffering is the responsibility of the caller, and {@link InputStream#markSupported} may be false. 51 | * @param stream a way to read process output which has not already been handled 52 | * @throws Exception if anything goes wrong, this watch is deactivated 53 | */ 54 | public abstract void output(@NonNull InputStream stream) throws Exception; 55 | 56 | /** 57 | * Notification that the process has exited or vanished. 58 | * {@link #output} should have been called with any final uncollected output. 59 | *
Any metadata associated with the process may be deleted after this call completes, rendering subsequent {@link Controller} calls unsatisfiable. 60 | *
Note that unlike {@link Controller#exitStatus(FilePath, Launcher)}, no specialized {@link Launcher} is available on the agent,
61 | * so if there are specialized techniques for determining process liveness they will not be considered here;
62 | * you still need to occasionally poll for an exit status from the master.
63 | * @param code the exit code, if known (0 conventionally represents success); may be negative for anomalous conditions such as a missing process
64 | * @param output standard output captured, if {@link DurableTask#captureOutput} was called; else null
65 | */
66 | @Asynchronous
67 | public abstract void exited(int code, @Nullable byte[] output) throws Exception;
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/durabletask/PowershellScript.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2017 Gabriel Loewen
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.durabletask;
26 |
27 | import edu.umd.cs.findbugs.annotations.NonNull;
28 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
29 | import hudson.*;
30 | import hudson.util.ListBoxModel;
31 | import hudson.util.ListBoxModel.Option;
32 |
33 | import java.io.InputStream;
34 | import java.util.ArrayList;
35 | import java.util.Arrays;
36 | import java.util.List;
37 | import hudson.model.TaskListener;
38 | import java.io.IOException;
39 |
40 | import jenkins.model.Jenkins;
41 | import org.apache.commons.lang.StringUtils;
42 | import org.kohsuke.accmod.Restricted;
43 | import org.kohsuke.accmod.restrictions.NoExternalUse;
44 | import org.kohsuke.stapler.DataBoundConstructor;
45 | import java.io.OutputStream;
46 | import java.nio.charset.Charset;
47 | import java.util.logging.Level;
48 | import java.util.logging.Logger;
49 |
50 | import org.kohsuke.stapler.DataBoundSetter;
51 |
52 | import edu.umd.cs.findbugs.annotations.NonNull;
53 |
54 | /**
55 | * Runs a Powershell script
56 | */
57 | public final class PowershellScript extends FileMonitoringTask {
58 | @SuppressFBWarnings("MS_SHOULD_BE_FINAL") // Used to control usage of binary or shell wrapper
59 | @Restricted(NoExternalUse.class)
60 | public static boolean USE_BINARY_WRAPPER = Boolean.getBoolean(PowershellScript.class.getName() + ".USE_BINARY_WRAPPER");
61 |
62 | private final String script;
63 | private String powershellBinary = "powershell";
64 | private boolean usesBom = true;
65 | private boolean loadProfile;
66 | private boolean capturingOutput;
67 | private static final Logger LOGGER = Logger.getLogger(PowershellScript.class.getName());
68 | private static final String LAUNCH_DIAGNOSTICS_PROP = PowershellScript.class.getName() + ".LAUNCH_DIAGNOSTICS";
69 |
70 | /**
71 | * Enables the debug flag for the binary wrapper.
72 | */
73 | @SuppressWarnings("FieldMayBeFinal")
74 | // TODO use SystemProperties if and when unrestricted
75 | private static boolean LAUNCH_DIAGNOSTICS = Boolean.getBoolean(LAUNCH_DIAGNOSTICS_PROP);
76 |
77 | @DataBoundConstructor public PowershellScript(String script) {
78 | this.script = script;
79 | }
80 |
81 | public String getPowershellBinary() {
82 | return powershellBinary;
83 | }
84 |
85 | @DataBoundSetter
86 | public void setPowershellBinary(String powershellBinary) {
87 | this.powershellBinary = powershellBinary;
88 | }
89 |
90 | public boolean isLoadProfile() {
91 | return loadProfile;
92 | }
93 |
94 | @DataBoundSetter
95 | public void setLoadProfile(boolean loadProfile) {
96 | this.loadProfile = loadProfile;
97 | }
98 |
99 | public String getScript() {
100 | return script;
101 | }
102 |
103 | @Override public void captureOutput() {
104 | capturingOutput = true;
105 | }
106 |
107 | @Override protected FileMonitoringController doLaunch(FilePath ws, Launcher launcher, TaskListener listener, EnvVars envVars) throws IOException, InterruptedException {
108 |
109 | FilePath nodeRoot = getNodeRoot(ws);
110 | AgentInfo agentInfo = getAgentInfo(nodeRoot);
111 | PowershellController c = new PowershellController(ws, envVars.get(COOKIE));
112 |
113 | List