Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
jenkins
提交
2524af13
J
jenkins
项目概览
xxadev
/
jenkins
与 Fork 源项目一致
从无法访问的项目Fork
通知
3
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
J
jenkins
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
2524af13
编写于
4月 18, 2014
作者:
K
Kohsuke Kawaguchi
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Added a new extension point for more pluggable JNLP slave handling
上级
a7eb8b7e
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
274 addition
and
107 deletion
+274
-107
changelog.html
changelog.html
+2
-0
core/src/main/java/jenkins/slaves/DefaultJnlpSlaveReceiver.java
...rc/main/java/jenkins/slaves/DefaultJnlpSlaveReceiver.java
+79
-0
core/src/main/java/jenkins/slaves/JnlpAgentReceiver.java
core/src/main/java/jenkins/slaves/JnlpAgentReceiver.java
+64
-0
core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol.java
.../src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol.java
+8
-38
core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol2.java
...src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol2.java
+3
-69
core/src/main/java/jenkins/slaves/JnlpSlaveHandshake.java
core/src/main/java/jenkins/slaves/JnlpSlaveHandshake.java
+118
-0
未找到文件。
changelog.html
浏览文件 @
2524af13
...
...
@@ -71,6 +71,8 @@ Upcoming changes</a>
(
<a
href=
"https://issues.jenkins-ci.org/browse/JENKINS-22583"
>
issue 22583
</a>
)
<li
class=
rfe
>
Update version of bundled Mailer plugin to 1.8 to avoid issues with older versions
<li
class=
rfe
>
Added a new extension point for more pluggable JNLP slave handling
</ul>
</div>
<!--=TRUNK-END=-->
...
...
core/src/main/java/jenkins/slaves/DefaultJnlpSlaveReceiver.java
0 → 100644
浏览文件 @
2524af13
package
jenkins.slaves
;
import
hudson.Extension
;
import
hudson.TcpSlaveAgentListener.ConnectionFromCurrentPeer
;
import
hudson.Util
;
import
hudson.model.Slave
;
import
hudson.remoting.Channel
;
import
hudson.slaves.SlaveComputer
;
import
jenkins.model.Jenkins
;
import
java.io.IOException
;
import
java.security.SecureRandom
;
import
java.util.Properties
;
import
java.util.concurrent.ExecutionException
;
import
java.util.concurrent.TimeUnit
;
import
java.util.concurrent.TimeoutException
;
import
java.util.logging.Logger
;
/**
* Match the name against the slave name and route the incoming JNLP agent as {@link Slave}.
*
* @author Kohsuke Kawaguchi
* @since 1.561
*/
@Extension
public
class
DefaultJnlpSlaveReceiver
extends
JnlpAgentReceiver
{
@Override
public
boolean
handle
(
String
nodeName
,
JnlpSlaveHandshake
handshake
)
throws
IOException
,
InterruptedException
{
SlaveComputer
computer
=
(
SlaveComputer
)
Jenkins
.
getInstance
().
getComputer
(
nodeName
);
if
(
computer
==
null
)
{
return
false
;
}
Channel
ch
=
computer
.
getChannel
();
if
(
ch
!=
null
)
{
String
c
=
handshake
.
getRequestProperty
(
"Cookie"
);
if
(
c
!=
null
&&
c
.
equals
(
ch
.
getProperty
(
COOKIE_NAME
)))
{
// we think we are currently connected, but this request proves that it's from the party
// we are supposed to be communicating to. so let the current one get disconnected
LOGGER
.
info
(
"Disconnecting "
+
nodeName
+
" as we are reconnected from the current peer"
);
try
{
computer
.
disconnect
(
new
ConnectionFromCurrentPeer
()).
get
(
15
,
TimeUnit
.
SECONDS
);
}
catch
(
ExecutionException
e
)
{
throw
new
IOException
(
"Failed to disconnect the current client"
,
e
);
}
catch
(
TimeoutException
e
)
{
throw
new
IOException
(
"Failed to disconnect the current client"
,
e
);
}
}
else
{
handshake
.
error
(
nodeName
+
" is already connected to this master. Rejecting this connection."
);
return
false
;
}
}
Properties
response
=
new
Properties
();
String
cookie
=
generateCookie
();
response
.
put
(
"Cookie"
,
cookie
);
handshake
.
success
(
response
);
// this cast is leaking abstraction
JnlpSlaveAgentProtocol2
.
Handler
handler
=
(
JnlpSlaveAgentProtocol2
.
Handler
)
handshake
;
ch
=
handler
.
jnlpConnect
(
computer
);
ch
.
setProperty
(
COOKIE_NAME
,
cookie
);
return
true
;
}
private
String
generateCookie
()
{
byte
[]
cookie
=
new
byte
[
32
];
new
SecureRandom
().
nextBytes
(
cookie
);
return
Util
.
toHexString
(
cookie
);
}
private
static
final
Logger
LOGGER
=
Logger
.
getLogger
(
DefaultJnlpSlaveReceiver
.
class
.
getName
());
private
static
final
String
COOKIE_NAME
=
JnlpSlaveAgentProtocol2
.
class
.
getName
()+
".cookie"
;
}
core/src/main/java/jenkins/slaves/JnlpAgentReceiver.java
0 → 100644
浏览文件 @
2524af13
package
jenkins.slaves
;
import
hudson.ExtensionList
;
import
hudson.ExtensionPoint
;
import
hudson.model.Slave
;
import
jenkins.model.Jenkins
;
import
java.io.IOException
;
import
java.util.Properties
;
/**
* Receives incoming slaves connecting through {@link JnlpSlaveAgentProtocol2}.
*
* <p>
* This is useful to establish the communication with other JVMs and use them
* for different purposes outside {@link Slave}s.
*
* @author Kohsuke Kawaguchi
* @since 1.561
*/
public
abstract
class
JnlpAgentReceiver
implements
ExtensionPoint
{
/**
* Called after the client has connected.
*
* <p>
* The implementation must do the following in the order:
*
* <ol>
* <li>Check if the implementation recognizes and claims the given name.
* If not, return false to let other {@link JnlpAgentReceiver} have a chance to
* take this connection.
*
* <li>If you claim the name but the connection is refused, call
* {@link JnlpSlaveHandshake#error(String)} to refuse the client, and return true.
* The connection will be shut down and the client will report this error to the user.
*
* <li>If you claim the name and the connection is OK, call
* {@link JnlpSlaveHandshake#success(Properties)} to accept the client.
*
* <li>Proceed to build a channel with {@link JnlpSlaveHandshake#createChannelBuilder(String)}
* and return true
*
* @param name
* Name of the incoming JNLP agent. All {@link JnlpAgentReceiver} shares a single namespace
* of names. The implementation needs to be able to tell which name belongs to them.
*
* @param handshake
* Encapsulation of the interaction with the incoming JNLP agent.
*
* @return
* true if the name was claimed and the handshake was completed (either successfully or unsuccessfully)
* false if the name was not claimed. Other {@link JnlpAgentReceiver}s will be called to see if they
* take this connection.
*
* @throws Exception
* Any exception thrown from this method will fatally terminate the connection.
*/
public
abstract
boolean
handle
(
String
name
,
JnlpSlaveHandshake
handshake
)
throws
IOException
,
InterruptedException
;
public
static
ExtensionList
<
JnlpAgentReceiver
>
all
()
{
return
Jenkins
.
getInstance
().
getExtensionList
(
JnlpAgentReceiver
.
class
);
}
}
core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol.java
浏览文件 @
2524af13
...
...
@@ -2,7 +2,6 @@ package jenkins.slaves;
import
hudson.AbortException
;
import
hudson.Extension
;
import
hudson.model.Computer
;
import
hudson.remoting.Channel
;
import
hudson.remoting.Channel.Listener
;
import
hudson.remoting.ChannelBuilder
;
...
...
@@ -16,7 +15,6 @@ import org.jenkinsci.remoting.nio.NioChannelHub;
import
javax.inject.Inject
;
import
java.io.BufferedWriter
;
import
java.io.DataInputStream
;
import
java.io.DataOutputStream
;
import
java.io.IOException
;
import
java.io.OutputStream
;
import
java.io.OutputStreamWriter
;
...
...
@@ -68,23 +66,7 @@ public class JnlpSlaveAgentProtocol extends AgentProtocol {
new
Handler
(
hub
.
getHub
(),
socket
).
run
();
}
protected
static
class
Handler
{
protected
final
NioChannelHub
hub
;
protected
final
Socket
socket
;
/**
* Wrapping Socket input stream.
*/
protected
final
DataInputStream
in
;
/**
* For writing handshaking response.
*
* This is a poor design choice that we just carry forward for compatibility.
* For better protocol design, {@link DataOutputStream} is preferred for newer
* protocols.
*/
protected
final
PrintWriter
out
;
protected
static
class
Handler
extends
JnlpSlaveHandshake
{
/**
* @deprecated as of 1.559
...
...
@@ -95,10 +77,9 @@ public class JnlpSlaveAgentProtocol extends AgentProtocol {
}
public
Handler
(
NioChannelHub
hub
,
Socket
socket
)
throws
IOException
{
this
.
hub
=
hub
;
this
.
socket
=
socket
;
in
=
new
DataInputStream
(
socket
.
getInputStream
());
out
=
new
PrintWriter
(
new
BufferedWriter
(
new
OutputStreamWriter
(
socket
.
getOutputStream
(),
"UTF-8"
)),
true
);
super
(
hub
,
socket
,
new
DataInputStream
(
socket
.
getInputStream
()),
new
PrintWriter
(
new
BufferedWriter
(
new
OutputStreamWriter
(
socket
.
getOutputStream
(),
"UTF-8"
)),
true
));
}
protected
void
run
()
throws
IOException
,
InterruptedException
{
...
...
@@ -106,19 +87,19 @@ public class JnlpSlaveAgentProtocol extends AgentProtocol {
final
String
nodeName
=
in
.
readUTF
();
if
(!
SLAVE_SECRET
.
mac
(
nodeName
).
equals
(
secret
))
{
error
(
out
,
"Unauthorized access"
);
error
(
"Unauthorized access"
);
return
;
}
SlaveComputer
computer
=
(
SlaveComputer
)
Jenkins
.
getInstance
().
getComputer
(
nodeName
);
if
(
computer
==
null
)
{
error
(
out
,
"No such slave: "
+
nodeName
);
error
(
"No such slave: "
+
nodeName
);
return
;
}
if
(
computer
.
getChannel
()!=
null
)
{
error
(
out
,
nodeName
+
" is already connected to this master. Rejecting this connection."
);
error
(
nodeName
+
" is already connected to this master. Rejecting this connection."
);
return
;
}
...
...
@@ -134,12 +115,7 @@ public class JnlpSlaveAgentProtocol extends AgentProtocol {
logw
.
println
(
"JNLP agent connected from "
+
socket
.
getInetAddress
());
try
{
ChannelBuilder
cb
;
if
(
hub
==
null
)
cb
=
new
ChannelBuilder
(
nodeName
,
Computer
.
threadPoolForRemoting
);
else
cb
=
hub
.
newChannelBuilder
(
nodeName
,
Computer
.
threadPoolForRemoting
);
ChannelBuilder
cb
=
createChannelBuilder
(
nodeName
);
computer
.
setChannel
(
cb
.
withHeaderStream
(
log
).
build
(
socket
),
log
,
new
Listener
()
{
...
...
@@ -165,12 +141,6 @@ public class JnlpSlaveAgentProtocol extends AgentProtocol {
throw
e
;
}
}
protected
void
error
(
PrintWriter
out
,
String
msg
)
throws
IOException
{
out
.
println
(
msg
);
LOGGER
.
log
(
Level
.
WARNING
,
Thread
.
currentThread
().
getName
()+
" is aborted: "
+
msg
);
socket
.
close
();
}
}
private
static
final
Logger
LOGGER
=
Logger
.
getLogger
(
JnlpSlaveAgentProtocol
.
class
.
getName
());
...
...
core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol2.java
浏览文件 @
2524af13
package
jenkins.slaves
;
import
hudson.Extension
;
import
hudson.TcpSlaveAgentListener.ConnectionFromCurrentPeer
;
import
hudson.Util
;
import
hudson.remoting.Channel
;
import
hudson.remoting.Engine
;
import
hudson.slaves.SlaveComputer
;
import
jenkins.model.Jenkins
;
import
org.jenkinsci.remoting.nio.NioChannelHub
;
import
java.io.ByteArrayInputStream
;
import
java.io.IOException
;
import
java.io.PrintWriter
;
import
java.net.Socket
;
import
java.security.SecureRandom
;
import
java.util.Map.Entry
;
import
java.util.Properties
;
import
java.util.concurrent.ExecutionException
;
import
java.util.concurrent.TimeUnit
;
import
java.util.concurrent.TimeoutException
;
import
java.util.logging.Logger
;
/**
* {@link JnlpSlaveAgentProtocol} Version 2.
...
...
@@ -62,69 +49,16 @@ public class JnlpSlaveAgentProtocol2 extends JnlpSlaveAgentProtocol {
*/
@Override
protected
void
run
()
throws
IOException
,
InterruptedException
{
Properties
request
=
new
Properties
();
request
.
load
(
new
ByteArrayInputStream
(
in
.
readUTF
().
getBytes
(
"UTF-8"
)));
final
String
nodeName
=
request
.
getProperty
(
"Node-Name"
);
if
(!
SLAVE_SECRET
.
mac
(
nodeName
).
equals
(
request
.
getProperty
(
"Secret-Key"
)))
{
error
(
out
,
"Unauthorized access"
);
return
;
}
SlaveComputer
computer
=
(
SlaveComputer
)
Jenkins
.
getInstance
().
getComputer
(
nodeName
);
if
(
computer
==
null
)
{
error
(
out
,
"No such slave: "
+
nodeName
);
return
;
}
Channel
ch
=
computer
.
getChannel
();
if
(
ch
!=
null
)
{
String
c
=
request
.
getProperty
(
"Cookie"
);
if
(
c
!=
null
&&
c
.
equals
(
ch
.
getProperty
(
COOKIE_NAME
)))
{
// we think we are currently connected, but this request proves that it's from the party
// we are supposed to be communicating to. so let the current one get disconnected
LOGGER
.
info
(
"Disconnecting "
+
nodeName
+
" as we are reconnected from the current peer"
);
try
{
computer
.
disconnect
(
new
ConnectionFromCurrentPeer
()).
get
(
15
,
TimeUnit
.
SECONDS
);
}
catch
(
ExecutionException
e
)
{
throw
new
IOException
(
"Failed to disconnect the current client"
,
e
);
}
catch
(
TimeoutException
e
)
{
throw
new
IOException
(
"Failed to disconnect the current client"
,
e
);
}
}
else
{
error
(
out
,
nodeName
+
" is already connected to this master. Rejecting this connection."
);
for
(
JnlpAgentReceiver
recv
:
JnlpAgentReceiver
.
all
())
{
if
(
recv
.
handle
(
nodeName
,
this
))
return
;
}
}
out
.
println
(
Engine
.
GREETING_SUCCESS
);
Properties
response
=
new
Properties
();
String
cookie
=
generateCookie
();
response
.
put
(
"Cookie"
,
cookie
);
writeResponseHeaders
(
out
,
response
);
ch
=
jnlpConnect
(
computer
);
ch
.
setProperty
(
COOKIE_NAME
,
cookie
);
}
private
void
writeResponseHeaders
(
PrintWriter
out
,
Properties
response
)
{
for
(
Entry
<
Object
,
Object
>
e
:
response
.
entrySet
())
{
out
.
println
(
e
.
getKey
()+
": "
+
e
.
getValue
());
}
out
.
println
();
// empty line to conclude the response header
}
private
String
generateCookie
()
{
byte
[]
cookie
=
new
byte
[
32
];
new
SecureRandom
().
nextBytes
(
cookie
);
return
Util
.
toHexString
(
cookie
);
error
(
"Unrecognized name: "
+
nodeName
);
}
}
private
static
final
Logger
LOGGER
=
Logger
.
getLogger
(
JnlpSlaveAgentProtocol2
.
class
.
getName
());
private
static
final
String
COOKIE_NAME
=
JnlpSlaveAgentProtocol2
.
class
.
getName
()+
".cookie"
;
}
core/src/main/java/jenkins/slaves/JnlpSlaveHandshake.java
0 → 100644
浏览文件 @
2524af13
package
jenkins.slaves
;
import
hudson.model.Computer
;
import
hudson.remoting.Channel
;
import
hudson.remoting.ChannelBuilder
;
import
hudson.remoting.Engine
;
import
org.jenkinsci.remoting.nio.NioChannelHub
;
import
java.io.DataInputStream
;
import
java.io.DataOutputStream
;
import
java.io.IOException
;
import
java.io.PrintWriter
;
import
java.net.Socket
;
import
java.util.Map.Entry
;
import
java.util.Properties
;
import
java.util.logging.Level
;
import
java.util.logging.Logger
;
/**
* Palette of objects to talk to the incoming JNLP slave connection.
*
* @author Kohsuke Kawaguchi
* @since 1.561
*/
public
class
JnlpSlaveHandshake
{
/**
* Useful for creating a {@link Channel} with NIO as the underlying transport.
*/
/*package*/
final
NioChannelHub
hub
;
/**
* Socket connection to the slave.
*/
/*package*/
final
Socket
socket
;
/**
* Wrapping Socket input stream.
*/
/*package*/
final
DataInputStream
in
;
/**
* For writing handshaking response.
*
* This is a poor design choice that we just carry forward for compatibility.
* For better protocol design, {@link DataOutputStream} is preferred for newer
* protocols.
*/
/*package*/
final
PrintWriter
out
;
/**
* Bag of properties the JNLP agent have sent us during the hand-shake.
*/
/*package*/
final
Properties
request
=
new
Properties
();
/*package*/
JnlpSlaveHandshake
(
NioChannelHub
hub
,
Socket
socket
,
DataInputStream
in
,
PrintWriter
out
)
{
this
.
hub
=
hub
;
this
.
socket
=
socket
;
this
.
in
=
in
;
this
.
out
=
out
;
}
public
NioChannelHub
getHub
()
{
return
hub
;
}
public
Socket
getSocket
()
{
return
socket
;
}
public
DataInputStream
getIn
()
{
return
in
;
}
public
PrintWriter
getOut
()
{
return
out
;
}
public
Properties
getRequestProperties
()
{
return
request
;
}
public
String
getRequestProperty
(
String
name
)
{
return
request
.
getProperty
(
name
);
}
/**
* Sends the error output and bail out.
*/
public
void
error
(
String
msg
)
throws
IOException
{
out
.
println
(
msg
);
LOGGER
.
log
(
Level
.
WARNING
,
Thread
.
currentThread
().
getName
()+
" is aborted: "
+
msg
);
socket
.
close
();
}
/**
* {@link JnlpAgentReceiver} calls this method to tell the client that the server
* is happy with the handshaking and is ready to move on to build a channel.
*/
public
void
success
(
Properties
response
)
{
out
.
println
(
Engine
.
GREETING_SUCCESS
);
for
(
Entry
<
Object
,
Object
>
e
:
response
.
entrySet
())
{
out
.
println
(
e
.
getKey
()+
": "
+
e
.
getValue
());
}
out
.
println
();
// empty line to conclude the response header
}
public
ChannelBuilder
createChannelBuilder
(
String
nodeName
)
{
if
(
hub
==
null
)
return
new
ChannelBuilder
(
nodeName
,
Computer
.
threadPoolForRemoting
);
else
return
hub
.
newChannelBuilder
(
nodeName
,
Computer
.
threadPoolForRemoting
);
}
private
static
final
Logger
LOGGER
=
Logger
.
getLogger
(
JnlpSlaveHandshake
.
class
.
getName
());
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录