23
23
*/
24
24
package hudson .slaves ;
25
25
26
+ import com .google .common .annotations .VisibleForTesting ;
26
27
import hudson .Extension ;
27
28
import hudson .FilePath ;
28
29
import jenkins .util .SystemProperties ;
34
35
import jenkins .security .MasterToSlaveCallable ;
35
36
import jenkins .slaves .PingFailureAnalyzer ;
36
37
38
+ import javax .annotation .CheckForNull ;
37
39
import java .io .IOException ;
38
40
import java .util .concurrent .atomic .AtomicBoolean ;
39
41
import java .util .logging .Level ;
@@ -86,28 +88,38 @@ public ChannelPinger() {
86
88
87
89
@ Override
88
90
public void preOnline (Computer c , Channel channel , FilePath root , TaskListener listener ) {
89
- install (channel );
91
+ SlaveComputer slaveComputer = null ;
92
+ if (c instanceof SlaveComputer ) {
93
+ slaveComputer = (SlaveComputer ) c ;
94
+ }
95
+ install (channel , slaveComputer );
90
96
}
91
97
92
98
public void install (Channel channel ) {
99
+ install (channel , null );
100
+ }
101
+
102
+ @ VisibleForTesting
103
+ /*package*/ void install (Channel channel , @ CheckForNull SlaveComputer c ) {
93
104
if (pingTimeoutSeconds < 1 || pingIntervalSeconds < 1 ) {
94
105
LOGGER .warning ("Agent ping is disabled" );
95
106
return ;
96
107
}
97
108
109
+ // set up ping from both directions, so that in case of a router dropping a connection,
110
+ // both sides can notice it and take compensation actions.
98
111
try {
99
112
channel .call (new SetUpRemotePing (pingTimeoutSeconds , pingIntervalSeconds ));
100
113
LOGGER .fine ("Set up a remote ping for " + channel .getName ());
101
114
} catch (Exception e ) {
102
- LOGGER .severe ( "Failed to set up a ping for " + channel .getName ());
115
+ LOGGER .log ( Level . SEVERE , "Failed to set up a ping for " + channel .getName (), e );
103
116
}
104
117
105
- // set up ping from both directions, so that in case of a router dropping a connection,
106
- // both sides can notice it and take compensation actions.
107
- setUpPingForChannel (channel , pingTimeoutSeconds , pingIntervalSeconds , true );
118
+ setUpPingForChannel (channel , c , pingTimeoutSeconds , pingIntervalSeconds , true );
108
119
}
109
120
110
- static class SetUpRemotePing extends MasterToSlaveCallable <Void , IOException > {
121
+ @ VisibleForTesting
122
+ /*package*/ static class SetUpRemotePing extends MasterToSlaveCallable <Void , IOException > {
111
123
private static final long serialVersionUID = -2702219700841759872L ;
112
124
@ Deprecated
113
125
private transient int pingInterval ;
@@ -121,7 +133,7 @@ static class SetUpRemotePing extends MasterToSlaveCallable<Void, IOException> {
121
133
122
134
@ Override
123
135
public Void call () throws IOException {
124
- setUpPingForChannel (Channel .current (), pingTimeoutSeconds , pingIntervalSeconds , false );
136
+ setUpPingForChannel (Channel .current (), null , pingTimeoutSeconds , pingIntervalSeconds , false );
125
137
return null ;
126
138
}
127
139
@@ -163,30 +175,36 @@ protected Object readResolve() {
163
175
}
164
176
}
165
177
166
- static void setUpPingForChannel (final Channel channel , int timeoutSeconds , int intervalSeconds , final boolean analysis ) {
178
+ @ VisibleForTesting
179
+ /*package*/ static void setUpPingForChannel (final Channel channel , final SlaveComputer computer , int timeoutSeconds , int intervalSeconds , final boolean analysis ) {
167
180
LOGGER .log (Level .FINE , "setting up ping on {0} with a {1} seconds interval and {2} seconds timeout" , new Object [] {channel .getName (), intervalSeconds , timeoutSeconds });
168
181
final AtomicBoolean isInClosed = new AtomicBoolean (false );
169
182
final PingThread t = new PingThread (channel , timeoutSeconds * 1000L , intervalSeconds * 1000L ) {
170
183
@ Override
171
184
protected void onDead (Throwable cause ) {
172
- try {
173
185
if (analysis ) {
174
186
analyze (cause );
175
187
}
176
- if (isInClosed .get ()) {
188
+ boolean inClosed = isInClosed .get ();
189
+ // Disassociate computer channel before closing it
190
+ if (computer != null ) {
191
+ Exception exception = cause instanceof Exception ? (Exception ) cause : new IOException (cause );
192
+ computer .disconnect (new OfflineCause .ChannelTermination (exception ));
193
+ }
194
+ if (inClosed ) {
177
195
LOGGER .log (Level .FINE ,"Ping failed after the channel " +channel .getName ()+" is already partially closed." ,cause );
178
196
} else {
179
197
LOGGER .log (Level .INFO ,"Ping failed. Terminating the channel " +channel .getName ()+"." ,cause );
180
- channel .close (cause );
181
198
}
182
- } catch (IOException e ) {
183
- LOGGER .log (Level .SEVERE ,"Failed to terminate the channel " +channel .getName (),e );
184
- }
185
199
}
186
200
/** Keep in a separate method so we do not even try to do class loading on {@link PingFailureAnalyzer} from an agent JVM. */
187
- private void analyze (Throwable cause ) throws IOException {
201
+ private void analyze (Throwable cause ) {
188
202
for (PingFailureAnalyzer pfa : PingFailureAnalyzer .all ()) {
189
- pfa .onPingFailure (channel ,cause );
203
+ try {
204
+ pfa .onPingFailure (channel , cause );
205
+ } catch (IOException ex ) {
206
+ LOGGER .log (Level .WARNING , "Ping failure analyzer " + pfa .getClass ().getName () + " failed for " + channel .getName (), ex );
207
+ }
190
208
}
191
209
}
192
210
@ Deprecated
0 commit comments