恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Rundeck Java Step 插件开发实战:从 Workflow Step 到 Remote Script Node Step(基于 example-java-step-plugin)
首页
资讯中心
/
Rundeck Java Step 插件开发实战:从 Workflow Step 到 Remote Script Node Step(基于 example-java-step-plugin)
Rundeck Java Step 插件开发实战:从 Workflow Step 到 Remote Script Node Step(基于 example-java-step-plugin)
发布时间:2026/10/6 2:42:11
运维任务调度后端【免费下载链接】rundeckEnable Self-Service Operations: Give specific users access to your existing tools, services, and scripts项目地址https://gitcode.com/gh_mirrors/ru/rundeck点击查看免费下载Rundeck 通过插件机制将工作流中的各类能力执行步骤、节点步骤、远程脚本步骤等开放给开发者。本文以仓库examples/example-java-step-plugin目录下的三个 Java 示例插件为骨架系统讲解 Rundeck 工作流步骤类插件的接口体系、属性定义方式编程式与注解式、PropertyScope 作用域机制、脚本生成以及异常与失败原因处理帮助读者掌握一套可直接照搬的 Java 插件开发范式。读完本文你将能基于 Rundeck 官方接口编写出可被 Rundeck GUI 识别、配置并在工作流中执行的自定义 Step 插件。示例目录结构与文件清单examples/example-java-step-plugin目录包含三个完整的示例插件工程目录布局如下build.gradle、gradle*——Gradle 构建文件与 wrapper 脚本gradlew、gradlew.bat、gradle/wrapper/src/main/java/com/dtolabs/rundeck/plugin/example——插件源码目录包含三个文件ExampleStepPlugin.java一个Workflow Step Plugin普通工作流步骤不针对特定节点执行ExampleNodeStepPlugin.java一个Node Step Plugin针对每个节点执行的步骤ExampleRemoteScriptNodeStepPlugin.java一个Remote Script Node Step Plugin为远端节点生成脚本/命令并执行。三个示例恰好对应 Rundeck 工作流中三种最常用的步骤插件类型覆盖了纯逻辑步骤与节点执行步骤两大应用场景。工程还自带gradle/wrapper/gradle-wrapper.propertieswrapper 版本为 Gradle 3.5说明只需./gradlew即可独立构建无需预先安装 Gradle。三种步骤插件接口分工与选择要理解示例插件的写法先要弄清它们各自实现的接口。三类接口均定义在 Rundeck 核心模块core中插件类型接口服务名常量核心方法典型用途Workflow StepStepPluginServiceNameConstants.WorkflowStepexecuteStep(context, configuration)在工作流层面执行逻辑不面向特定节点Node StepNodeStepPluginServiceNameConstants.WorkflowNodeStepexecuteNodeStep(context, configuration, entry)对工作流中的每个目标节点分别执行逻辑Remote Script Node StepRemoteScriptNodeStepPluginServiceNameConstants.RemoteScriptNodeStepgenerateScript(context, configuration, entry)生成脚本/命令字符串交由 Rundeck 在远端节点执行接口源码位置StepPlugin.javaexecuteStep(PluginStepContext, MapString, Object)出错时抛出StepExceptionNodeStepPlugin.javaexecuteNodeStep(PluginStepContext, MapString, Object, INodeEntry)出错时抛出NodeStepExceptionRemoteScriptNodeStepPlugin.javagenerateScript(...)返回GeneratedScript出错时抛出NodeStepException。三类插件如何选择从接口签名可以看出分工差异Step Plugin 只拿到上下文与配置适合做与具体节点无关的编排、校验、汇总类逻辑Node Step Plugin 额外拿到INodeEntry目标节点可以读取节点的 hostname 等属性并针对该节点执行操作Remote Script Node Step Plugin则是把生成要执行的脚本这件事交给插件Rundeck 负责把生成的脚本分发到远端节点运行适合用一段动态生成的 Shell 命令完成跨节点操作。声明插件身份Plugin 注解与服务名每个示例类上都标注了Plugin注解用于向 Rundeck 声明这是一个插件、属于哪个服务。例如 ExampleStepPlugin.javaPlugin(name ExampleStepPlugin.SERVICE_PROVIDER_NAME, service ServiceNameConstants.WorkflowStep) public class ExampleStepPlugin implements StepPlugin, Describable { public static final String SERVICE_PROVIDER_NAME com.dtolabs.rundeck.plugin.example.ExampleStepPlugin;两个要素缺一不可service声明插件归属的 Rundeck 服务类型。源码注释与 ServiceNameConstants.java 建议使用ServiceNameConstants中预定义的常量避免手写字符串出错。该常量类中与步骤相关的服务名包括WorkflowStep、WorkflowNodeStep、RemoteScriptNodeStep同时还有NodeExecutor、FileCopier、Notification、LogFilter、Orchestrator等其它服务名是开发各类 Rundeck 插件的统一服务名出处name即 provider name插件的唯一标识。源码注释特别强调建议使用完全限定fully qualified的包风格名称这与 Java 命名空间约定一致可避免多个插件间的名称冲突。用 DescriptionBuilder 编程式构建插件描述ExampleStepPluginRundeck GUI 中插件的标题、说明和配置表单都来自插件提供的Description。ExampleStepPlugin实现了Describable接口在getDescription()方法中通过DescriptionBuilderPropertyBuilder完全用代码构建描述。完整代码示例ExampleStepPlugin.java 中定义了一个名为 Example Step、描述为 Does nothing 的插件并逐个声明了 6 个配置属性public Description getDescription() { return DescriptionBuilder.builder() .name(SERVICE_PROVIDER_NAME) .title(Example Step) .description(Does nothing) .property(PropertyBuilder.builder() .string(bunny) .title(Bunny) .description(Bunny name) .required(true) .build() ) .property(PropertyBuilder.builder() .booleanType(lampkin) .title(Lampkin) .description(Want Lampkin?) .required(false) .defaultValue(false) .build() ) .property(PropertyBuilder.builder() .freeSelect(color) .title(Color) .description(Your color) .required(false) .defaultValue(Blue) .values(Blue, Beige, Black) .build() ) .property(PropertyBuilder.builder() .integer(many) .title(Many) .description(How many?) .required(false) .defaultValue(2) .build() ) .property(PropertyBuilder.builder() .longType(cramp) .title(Cramp) .description(How crampy more?) .required(false) .defaultValue(20) .build() ) .property(PropertyBuilder.builder() .select(rice) .title(Rice Cream) .description(Rice Cream Flavor) .required(false) .values(Flambe, Crambo) .build() ) .build(); }PropertyBuilder 支持的属性类型上例演示了PropertyBuilder的 6 种典型属性构造方法覆盖了 Rundeck 插件配置表单的常用控件构造方法对应类型表单表现示例字段关键参数.string(bunny)String单行文本输入框bunny必填required(true).booleanType(lampkin)Boolean开关/勾选框lampkin默认 falsedefaultValue(false).freeSelect(color)String可自由输入的下拉下拉 可输入color默认 Blue可选 Blue/Beige/Blackvalues(...) 默认值.integer(many)Integer数字输入many默认 2defaultValue(2).longType(cramp)Long数字输入cramp默认 20defaultValue(20).select(rice)String仅可下拉选择纯下拉选择rice可选 Flambe/Crambo无默认值values(...)注意freeSelect与select的区别前者允许用户既从预置列表中选值、也可以自由输入任意文本后者只能从预置列表中选择。每个属性还都可以配置titleGUI 显示名、description帮助说明、required是否必填与defaultValue默认值以字符串形式传入。executeStep真正的业务逻辑getDescription只负责描述执行逻辑在executeStep中。插件运行时Rundeck 会把执行上下文与配置值传入public void executeStep(final PluginStepContext context, final MapString, Object configuration) throws StepException { System.out.println(Example step executing on nodes: context.getNodes().getNodeNames()); System.out.println(Example step configuration: configuration); System.out.println(Example step num: context.getStepNumber()); System.out.println(Example step context: context.getStepContext()); if (true.equals(configuration.get(lampkin))) { throw new StepException(lampkin was true, Reason.ExampleReason); } }这里的PluginStepContext提供了步骤运行所需的环境信息示例中用到了三类getNodes().getNodeNames()——本次工作流涉及的节点集合getStepNumber()——当前步骤在工作流中的序号getStepContext()——步骤上下文详情。失败原因FailureReason 与 StepException示例中定义了内部枚举来标识插件特有的失败原因static enum Reason implements FailureReason { ExampleReason }当配置lampkin为true时插件主动抛出StepException并附带失败原因Reason.ExampleReason。Rundeck 的StepException语义见 StepPlugin.java 注释要求抛出的异常应通过 failureReason 指明失败原因这样执行引擎才能把失败归类、呈现给用户。为每种典型失败定义一个实现FailureReason的枚举成员是 Rundeck 插件的推荐做法。实现 Node Step PluginexecuteNodeStep 与 DescriptionBuilder.CollaboratorExampleNodeStepPlugin演示的是节点级步骤工作流针对 N 个节点分发执行时每个节点都会触发一次executeNodeStep。它与前者的差异体现在两点实现NodeStepPluginDescriptionBuilder.Collaborator以及执行方法多一个节点参数。通过 Collaborator 参与描述构建与Describable不同该类通过实现DescriptionBuilder.Collaborator接口、覆写buildWith(DescriptionBuilder)方法来填充描述。Rundeck 在构建插件的Description时会回调该方法因此业务类无需自己持有 Description 对象ExampleNodeStepPlugin.java 中同样声明了 6 个属性monkey必填字符串、pancake布尔、yogurt自由选择、count整数、index长整数、icecream单选写法与ExampleStepPlugin完全一致只是没有调用.build()而是直接以链式调用结尾。executeNodeStep面向单个节点执行ExampleNodeStepPlugin.java 的执行方法多了一个INodeEntry entry参数——这就是当前要执行的目标节点public void executeNodeStep(final PluginStepContext context, final MapString, Object configuration, final INodeEntry entry) throws NodeStepException { System.out.println(Example node step executing on node: entry.getNodename()); System.out.println(Example step extra config: configuration); System.out.println(Example step num: context.getStepNumber()); System.out.println(Example step context: context.getStepContext()); if (true.equals(configuration.get(pancake))) { throw new NodeStepException(pancake was true, Reason.PancakeReason, entry.getNodename()); } }源码注释给出了明确指引插件应使用节点的属性如 hostname 或任何插件需要的属性来决定在该节点上的具体动作。INodeEntry提供了getNodename()等方法读取节点信息。失败时抛出NodeStepException其构造参数除了消息与FailureReason示例中的PancakeReason外还携带当前节点名便于 Rundeck 定位是哪个节点上的步骤失败。Remote Script Node Step Plugin注解式属性与脚本生成ExampleRemoteScriptNodeStepPlugin是三个示例中最完整、也最能体现声明式开发的一个它用字段注解定义配置属性并展示了脚本生成、属性作用域、描述修改与自定义校验器四个高级特性。用 PluginProperty 注解声明属性区别于编程式该插件没有在buildWith里逐个用PropertyBuilder声明属性而是直接在实例字段上加注解。Rundeck 会在运行时自动把配置值注入到这些字段中省去了手工从configurationMap 取值的样板代码。字段声明如下ExampleRemoteScriptNodeStepPlugin.javaPluginProperty(title Funky, description Funk name, required true) protected String funky; PluginProperty(title Thesis, description Thesis) TextArea protected String thesis; PluginProperty(title Jam, description Want jam?) protected boolean jam; PluginProperty(title Amount, description How amount?, defaultValue 2) protected int amount; PluginProperty(title Money, description how money?, defaultValue 20) protected long money; PluginProperty(title Fruit, description your fruit, defaultValue banana, scope PropertyScope.Instance) SelectValues(values {banana, lemon, orange}, freeSelect true) protected String fruit; PluginProperty(title Cake, description Cake flavor) SelectValues(values {vanilla, chocolate}, freeSelect false) protected String cake; PluginProperty(title Debug, description Turn on debug?, scope PropertyScope.Project) protected boolean debug;这里用到了四个核心注解PluginProperty声明字段为插件属性可配置title、description、defaultValue、required、scope字段类型自动决定属性类型String、boolean、int、long等TextArea把字符串属性渲染为多行文本域SelectValues提供预置值下拉列表freeSelect true时允许用户自由输入如fruitfalse时只能选择如cakePluginDescription类级注解声明插件在 GUI 中显示的标题与描述。同时类上仍保留Plugin(name..., serviceServiceNameConstants.RemoteScriptNodeStep)并声明了包风格的SERVICE_PROVIDER_NAME常量三者共同完成插件的服务注册与 GUI 展示定义。PropertyScope属性值的作用域查找机制该插件是理解PropertyScope的最佳教材。源码注释完整说明了作用域机制ExampleRemoteScriptNodeStepPlugin.java属性值在运行时按由窄到宽的优先级查找找不到时向更宽的作用域扩展存在特例。作用域从窄到宽依次为Instance实例——工作流步骤中为该步骤设置的值Project项目——项目配置属性中设置的值Framework框架/应用级——Rundeck 应用全局配置属性中设置的值。两个特殊作用域InstanceOnly与ProjectOnly禁止向更宽作用域扩展查找即值必须存在于该作用域本身。示例中的两个典型用法fruit显式声明scope PropertyScope.Instance属于步骤实例级配置会在步骤 GUI 中显示debug声明scope PropertyScope.Project不会在步骤 GUI 中显示但可在项目或框架级配置中提供取值。注意一个实用细节默认作用域是InstanceOnly且只有Instance/InstanceOnly作用域的属性会在工作流步骤 GUI 中显示。因此希望用户在配置步骤时填写的属性应保持默认或显式设为 Instance 作用域希望由项目/框架统一管理的属性如开关类调试标志则应设为 Project 作用域。buildWith修改描述、覆写属性、注入自定义校验器与ExampleNodeStepPlugin一样该类实现DescriptionBuilder.Collaborator但buildWith的用途更进阶——它演示了三件注解无法直接完成的事ExampleRemoteScriptNodeStepPlugin.java覆写注解描述用builder.title(...)、builder.description(...)替换PluginDescription的默认展示文案获取并修改已存在的属性builder.property(money)返回注解已声明的money属性对象随后可链式修改其描述并为其设置自定义校验器——示例中的校验器要求值必须是 15 之间的整数否则抛出ValidationException带原因文案这保证了 GUI 表单提交与命令行调用都能得到一致的校验反馈新增不绑定实例字段的属性用PropertyBuilder创建名为fakey的全新属性运行时其值会出现在传给插件方法的configurationMap 中。generateScript生成脚本或命令RemoteScriptNodeStepPlugin不直接执行动作而是把要在远端节点上跑什么交给generateScript返回的GeneratedScript。示例展示了两种生成方式ExampleRemoteScriptNodeStepPlugin.javapublic GeneratedScript generateScript(final PluginStepContext context, final MapString, Object configuration, final INodeEntry entry) { if (debug) { System.err.println(DEBUG for ExampleRemoteScriptNodeStepPlugin is true); } if (jam) { return GeneratedScriptBuilder.script( #!/bin/bash\n echo this is node entry.getNodename() \n echo stepnum context.getStepNumber() \n echo step context context.getStepContext() \n echo funky is funky \n echo fruit is fruit \n echo amount is amount \n echo money is money \n echo cake is cake \n echo extra: configuration \n echo thesis: thesis.replaceAll(, \\) \n , null ); } else { return GeneratedScriptBuilder.command(echo, context.getStepNumber() context.getStepContext() Hi funky is ( funky ) jam is jam ); } }要点解读GeneratedScriptBuilder.script(...)返回一段完整的脚本字符串这里以#!/bin/bash开头逐行 echo 出节点名、步骤号、步骤上下文与所有已注入的配置字段值GeneratedScriptBuilder.command(...)返回一个命令及参数相当于在远端执行一条echo命令由于属性已通过注解绑定到字段方法体内直接引用funky、fruit、amount、money、cake、thesis、jam、debug等字段即可无需再从configurationMap 中取值——这就是注解式属性定义与编程式定义相比的便利性thesis.replaceAll(, \\)演示了把多行文本安全地嵌入 Shell 单引号时的转义技巧值得在拼接脚本时复用。两种属性定义方式对比与选择综合三个示例Rundeck Java 插件声明属性存在两条等价路径维度编程式DescriptionBuilder PropertyBuilder注解式PluginProperty 等代表实现ExampleStepPlugin / ExampleNodeStepPluginExampleRemoteScriptNodeStepPlugin属性声明位置getDescription()或buildWith()方法内类实例字段上运行时取值从executeStep/executeNodeStep的configurationMap 中读取自动注入到注解字段直接引用动态生成属性支持完全代码控制弱属性在编译期确定代码量每个属性需完整 builder 链一行注解即可高级能力可任意增删改属性可与buildWith配合覆写/校验从源码结构看两者的最终产物都是同一个Description模型DescriptionBuilder与PluginProperty均围绕Property构建描述因此可以按需混用用注解声明常规属性用buildWith补充校验器与动态属性——这正是第三个示例展示的最佳实践组合。构建与运行该工程自带 Gradle Wrappergradle-wrapper.properties 指定 Gradle 3.5因此在任意环境下可执行cd examples/example-java-step-plugin ./gradlew buildWindows 环境使用gradlew.bat。构建产物为 jar 包放入 Rundeck 的libext插件目录即可被识别加载。Rundeck 通过Plugin注解中的 service 名将 jar 归入对应服务WorkflowStep / WorkflowNodeStep / RemoteScriptNodeStep随后便可在编辑作业 → 工作流 → 添加步骤中看到这三个示例插件为其配置属性并加入工作流执行。与其它 Java 插件示例的呼应仓库examples下还有一组平行的 Java 插件工程与本主题形成完整的插件开发知识面example-java-nodeexecutor-pluginNodeExecutor 服务负责真正在节点上执行命令example-java-notification-pluginNotification 服务处理作业完成/失败等事件通知example-java-logging-pluginsStreamingLogReader/Writer自定义执行日志的读取与写入example-java-storage-plugin 与 example-java-storage-converter-plugin密钥存储及其格式转换example-java-execution-lifecyle-plugin 与 example-java-job-lifecycle-plugin执行/作业生命周期钩子example-java-audit-plugin审计事件监听。这些工程的Plugin注解、ServiceNameConstants服务名与描述构建模式完全一致可互相参考。此外example-groovy-log-filter-plugins、example-script-node-step-plugin基于plugin.yaml的脚本式步骤插件等示例展示了同一机制在 Groovy 与纯脚本领域的实现说明Step 插件只是 Rundeck 插件服务体系中的一环掌握了本文的接口、注解与服务名体系后开发其它类型的插件只是换一组接口与ServiceNameConstants常量而已。小结本文以examples/example-java-step-plugin为线索完整覆盖了 Rundeck Java 步骤插件的开发全流程Plugin声明服务与名称 → 用DescriptionBuilder/PluginProperty定义 GUI 配置表单 → 用PropertyScope控制属性取值层级 → 在executeStep/executeNodeStep中实现逻辑并通过FailureReason 异常报告失败 → 或在generateScript中生成远程脚本。三个示例恰好构成一套最小但完整的模板读者可以直接以 ExampleStepPlugin.java、ExampleNodeStepPlugin.java 与 ExampleRemoteScriptNodeStepPlugin.java 为起点替换业务逻辑与属性定义即可产出生产可用的 Rundeck 自定义步骤插件。赞分享运维任务调度后端【免费下载链接】rundeckEnable Self-Service Operations: Give specific users access to your existing tools, services, and scripts项目地址https://gitcode.com/gh_mirrors/ru/rundeck点击查看免费下载相关推荐Inspektor Gadget架构深度解析从内核到云端的完整链路Inspektor Gadget架构深度解析从内核到云端的完整链路 Inspektor Gadget是一套基于eBPF技术的Kubernetes集群和LinuApache bRPC 发布 Apache Release 版本全流程从 Release Notes 到 ANNOUNCE 的 Step-by-Step 实战指南Apache bRPC 发布 Apache Release 版本全流程从 Release Notes 到 ANNOUNCE 的 Step by Step 实战后端RPC框架通信网络上一篇Thunderbird for Android 的 IMAP IDLE 推送实现新账号收件箱默认启用 Push 的技术方案解析下一篇ShowDoc 内置 Doctrine Inflector 实战指南PHP 单词复数化、单复数转换与命名风格变形完全解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考