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

posted on 2024-05-12 06:55  荣锋亮  阅读(45)  评论(0编辑  收藏  举报

导航