jenkins maven-hpi-plugin 插件run 内部处理简单说明
jenkins maven-hpi-plugin 是方便进行jenkins plugin 开发的一个maven plugin,提供了打包,测试,以及本地运行的能力,以下简单说明下
关于run 命令的处理
内部处理
实现上hpi:run 命令复用了jetty maven 插件的一些能力,对于我们提供了比较方便的本地运行能力,本地运行会先进行构建(hpi 打包),然后下载jenkins war 包,下载一些依赖的插件,并copy 到jenkins home 中的plugins 目录中(必备依赖插件,当然还有我们自己开发的插件)
之后就是启动jetty 服务
- run 内部处理
参考类图如下,可以看出是继承了jetty 的run war mojo
- 内部处理
关于run maven 插件的参数部分我就不介绍了,基本就是一个依赖参数,比如path,jenkins home ,端口定义
public void execute() throws MojoExecutionException, MojoFailureException {
openInternalPackagesIfRequired();
getProject().setArtifacts(resolveDependencies(dependencyResolution));
File basedir = getProject().getBasedir();
if (webApp == null || webApp.getContextPath() == null) {
if (contextPath != null) {
getLog().warn(
"Please use `webApp/contextPath` configuration parameter in place of the deprecated `contextPath` parameter");
if (webApp == null) {
try {
webApp = new MavenWebAppContext();
} catch (Exception e) {
throw new MojoExecutionException("Failed to initialize webApp configuration", e);
}
}
webApp.setContextPath(contextPath);
}
}
// jenkins home 设置
// compute jenkinsHome
if (jenkinsHome == null) {
if (hudsonHome != null) {
getLog().warn(
"Please use the `jenkinsHome` configuration parameter in place of the deprecated `hudsonHome` parameter");
jenkinsHome = hudsonHome;
}
String h = System.getenv("JENKINS_HOME");
if (h == null) {
h = System.getenv("HUDSON_HOME");
}
if (h != null && !h.isEmpty() && /* see pom.xml override */ !h.equals("null")) {
jenkinsHome = new File(h);
} else {
jenkinsHome = new File(basedir, "work");
}
}
// auto-enable stapler trace, unless otherwise configured already.
setSystemPropertyIfEmpty("stapler.trace", "true");
// allow Jetty to accept a bigger form so that it can handle update center JSON post
setSystemPropertyIfEmpty(ContextHandler.MAX_FORM_CONTENT_SIZE_KEY, "-1");
// general-purpose system property so that we can tell from Jenkins if we are running in the hpi:run mode.
setSystemPropertyIfEmpty("hudson.hpi.run", "true");
// expose the current top-directory of the plugin
setSystemPropertyIfEmpty("jenkins.moduleRoot", basedir.getAbsolutePath());
// look for jenkins.war
Artifacts jenkinsArtifacts = Artifacts.of(getProject())
.groupIdIs("org.jenkins-ci.main", "org.jvnet.hudson.main")
.artifactIdIsNot("remoting"); // remoting moved to its own release cycle
// jenkins war 的下载处理
if (webAppFile == null) {
Artifact jenkinsWarArtifact =
MavenArtifact.resolveArtifact(getJenkinsWarArtifact(), project, session, repositorySystem);
webAppFile = jenkinsWarArtifact.getFile();
if (webAppFile == null || !webAppFile.isFile()) {
throw new MojoExecutionException("Could not find " + webAppFile + " from " + jenkinsWarArtifact);
}
}
// make sure all the relevant Jenkins artifacts have the same version
for (Artifact a : jenkinsArtifacts) {
Artifact ba = jenkinsArtifacts.get(0);
if (!a.getVersion().equals(ba.getVersion())) {
throw new MojoExecutionException("Version of " + a.getId() + " is inconsistent with " + ba.getId());
}
}
// set JENKINS_HOME
setSystemPropertyIfEmpty("JENKINS_HOME", jenkinsHome.getAbsolutePath());
File pluginsDir = new File(jenkinsHome, "plugins");
try {
Files.createDirectories(pluginsDir.toPath());
} catch (IOException e) {
throw new MojoExecutionException("Failed to create directories for '" + pluginsDir + "'", e);
}
// enable view auto refreshing via stapler
setSystemPropertyIfEmpty("stapler.jelly.noCache", "true");
List<Resource> res = getProject().getBuild().getResources();
if (!res.isEmpty()) {
// pick up the first one and use it
Resource r = res.get(0);
setSystemPropertyIfEmpty("stapler.resourcePath", r.getDirectory());
}
// 生成项目的hpi 插件
generateHpl();
// 依赖插件的copy
// copy other dependency Jenkins plugins
try {
for (MavenArtifact a : getProjectArtifacts()) {
if (!a.isPluginBestEffort(getLog())) {
continue;
}
// find corresponding .hpi file
Artifact hpi =
artifactFactory.createArtifact(a.getGroupId(), a.getArtifactId(), a.getVersion(), null, "hpi");
hpi = MavenArtifact.resolveArtifact(hpi, project, session, repositorySystem);
// check recursive dependency. this is a rare case that happens when we split out some things from the
// core into a plugin
if (hasSameGavAsProject(hpi)) {
continue;
}
if (hpi.getFile().isDirectory()) {
throw new UnsupportedOperationException(
hpi.getFile() + " is a directory and not packaged yet. this isn't supported");
}
File upstreamHpl = pluginWorkspaceMap.read(hpi.getId());
String actualArtifactId = a.getActualArtifactId();
if (actualArtifactId == null) {
throw new MojoExecutionException(
"Failed to load actual artifactId from " + a + " ~ " + a.getFile());
}
if (upstreamHpl != null) {
copyHpl(upstreamHpl, pluginsDir, actualArtifactId);
} else {
copyPlugin(hpi.getFile(), pluginsDir, actualArtifactId);
}
}
} catch (IOException e) {
throw new MojoExecutionException("Unable to copy dependency plugin", e);
}
if (System.getProperty("java.util.logging.config.file") == null) {
// see org.apache.juli.logging.DirectJDKLog
System.setProperty("org.apache.juli.formatter", SupportLogFormatter.class.getName());
}
if (loggers != null) {
for (Handler h : LogManager.getLogManager().getLogger("").getHandlers()) {
if (h instanceof ConsoleHandler) {
h.setLevel(Level.ALL);
}
}
loggerReferences = new LinkedList<>();
for (Map.Entry<String, String> logger : loggers.entrySet()) {
Logger l = Logger.getLogger(logger.getKey());
loggerReferences.add(l);
l.setLevel(Level.parse(logger.getValue()));
}
}
super.execute();
}
- generateHpl 的处理
这部分比较有意思,官方是直接使用了maven 执行插件的模式,又直接调用的maven-hpi-plugin 只是goal 是hpi
private void generateHpl() throws MojoExecutionException {
MojoExecutor.executeMojo(
MojoExecutor.plugin(
MojoExecutor.groupId("org.jenkins-ci.tools"), MojoExecutor.artifactId("maven-hpi-plugin")),
MojoExecutor.goal("hpl"),
MojoExecutor.configuration(
MojoExecutor.element(MojoExecutor.name("jenkinsHome"), jenkinsHome.toString()),
MojoExecutor.element(MojoExecutor.name("pluginName"), project.getName()),
MojoExecutor.element(MojoExecutor.name("warSourceDirectory"), warSourceDirectory.toString()),
MojoExecutor.element(MojoExecutor.name("jenkinsCoreId"), jenkinsCoreId),
MojoExecutor.element(
MojoExecutor.name("pluginFirstClassLoader"), Boolean.toString(pluginFirstClassLoader)),
MojoExecutor.element(MojoExecutor.name("maskClasses"), maskClasses)),
MojoExecutor.executionEnvironment(project, session, pluginManager));
}
- 开发模式的处理
部署过jenkins 的都了解,默认jenkins 是有认证的,jenkins 为了方便基于hpi 环境的调试,提供了几个参数可以方便本地debug
核心是hudson.Main.development=true系统属性,比如我们使用如下命令启动jenkins 默认就没有认证了,比较适合测试,对于实际
以及运行的系统如果希望禁用认证就需要自己修改config.xml (jenkins_home 文件下,详细的参考链接)
# 从测试上hudson.hpi.run 似乎没有,核心还是hudson.Main.development=true
java -Dhudson.hpi.run=true -Dhudson.Main.development=true -jar /usr/share/java/jenkins.war
所以默认jenkins 脚手架工具对于maven-hpi-plugin的系统参数设置为了true,如下
<plugin>
<groupId>org.jenkins-ci.tools</groupId>
<artifactId>maven-hpi-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<webApp>
<contextPath>/jenkins</contextPath>
</webApp>
<systemProperties>
<hudson.Main.development>${hudson.Main.development}</hudson.Main.development>
</systemProperties>
</configuration>
</plugin>
当然Jenkins内部会有一个处理
core/src/main/java/jenkins/install/InstallUtil.java 有判断
if (!shouldRun) {
if (Functions.getIsUnitTest()) {
return InstallState.TEST;
}
if (SystemProperties.getBoolean("hudson.Main.development")) {
return InstallState.DEVELOPMENT;
}
}
此时生成的config.xml
<?xml version='1.1' encoding='UTF-8'?>
<hudson>
<disabledAdministrativeMonitors/>
<version>2.440.3</version>
<numExecutors>2</numExecutors>
<mode>NORMAL</mode>
<authorizationStrategy class="hudson.security.AuthorizationStrategy$Unsecured"/>
<securityRealm class="hudson.security.SecurityRealm$None"/>
<disableRememberMe>false</disableRememberMe>
<projectNamingStrategy class="jenkins.model.ProjectNamingStrategy$DefaultProjectNamingStrategy"/>
<workspaceDir>${JENKINS_HOME}/workspace/${ITEM_FULL_NAME}</workspaceDir>
<buildsDir>${ITEM_ROOTDIR}/builds</buildsDir>
<jdks/>
<viewsTabBar class="hudson.views.DefaultViewsTabBar"/>
<myViewsTabBar class="hudson.views.DefaultMyViewsTabBar"/>
<clouds/>
<scmCheckoutRetryCount>0</scmCheckoutRetryCount>
<views>
<hudson.model.AllView>
<owner class="hudson" reference="../../.."/>
<name>all</name>
<filterExecutors>false</filterExecutors>
<filterQueue>false</filterQueue>
<properties class="hudson.model.View$PropertyList"/>
</hudson.model.AllView>
</views>
<primaryView>all</primaryView>
<slaveAgentPort>0</slaveAgentPort>
<label></label>
<crumbIssuer class="hudson.security.csrf.DefaultCrumbIssuer">
<excludeClientIPFromCrumb>false</excludeClientIPFromCrumb>
</crumbIssuer>
<nodeProperties/>
<globalNodeProperties/>
<nodeRenameMigrationNeeded>false</nodeRenameMigrationNeeded>
说明
以上是一个简单说明,通过maven-hpi-plugin 我们可以了解jenkins 插件的本地开发是如何运行的,
参考资料
core/src/main/java/hudson/Functions.java
core/src/main/java/jenkins/install/InstallUtil.java
https://github.com/jenkinsci/jenkins
https://github.com/jenkinsci/plugin-pom
https://www.jenkins.io/doc/book/security/access-control/disable/
https://github.com/jenkinsci/maven-hpi-plugin