<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Spinnaker – Plugin Creators</title>
    <link>https://deploy-preview-629--spinnaker-io.netlify.app/docs/community/contributing/code/developer-guides/plugin-creators/</link>
    <description>Recent content in Plugin Creators on Spinnaker</description>
    <generator>Hugo -- gohugo.io</generator>
    
	  <atom:link href="https://deploy-preview-629--spinnaker-io.netlify.app/docs/community/contributing/code/developer-guides/plugin-creators/index.xml" rel="self" type="application/rss+xml" />
    
    
      
        
      
    
    
    <item>
      <title>Docs: Plugin Creators Overview</title>
      <link>https://deploy-preview-629--spinnaker-io.netlify.app/docs/community/contributing/code/developer-guides/plugin-creators/overview/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://deploy-preview-629--spinnaker-io.netlify.app/docs/community/contributing/code/developer-guides/plugin-creators/overview/</guid>
      <description>
        
        
        &lt;div class=&#34;alert alert-warning&#34; role=&#34;alert&#34;&gt;

  &lt;p&gt;
	 
    The contents of this page refer to alpha features in Spinnaker
    1.19.4.
	 
  &lt;/p&gt;
  &lt;p&gt;
    This means we are working on their stability and usability, as well as
    possibly adding or changing features. Expect rough edges, and file &lt;a
    href=&#34;https://github.com/spinnaker/spinnaker/issues&#34;&gt;issues&lt;/a&gt; as needed.
  &lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;requirements&#34;&gt;Requirements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Spinnaker v2025.3.0+&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;taxonomy&#34;&gt;Taxonomy&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Extension Point&lt;/strong&gt;: Defined by a Spinnaker service, it represents an official contract for which extensions can be
written against. Defined by using an &lt;code&gt;org.pf4j.ExtensionPoint&lt;/code&gt; interface. Written by Core Contributors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extension&lt;/strong&gt;: A piece of code that implements an Extension Point. Written by Developers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Plugin&lt;/strong&gt;: A collection of Extensions that can be bundled together and shipped as a single artifact. Also written by
Developers.&lt;/p&gt;
&lt;h2 id=&#34;plugin-overview&#34;&gt;Plugin overview&lt;/h2&gt;
&lt;p&gt;A plugin enables an operator to extend Spinnaker with custom functionality. Use cases include fetching credentials from
a custom authorization service, adding a wait stage to a pipeline, updating a Jira ticket, and sending Echo events to
third-party tools.&lt;/p&gt;
&lt;h2 id=&#34;extensionpoint-plugins&#34;&gt;ExtensionPoint plugins&lt;/h2&gt;
&lt;p&gt;Spinnaker uses the 
&lt;a href=&#34;https://github.com/pf4j/pf4j&#34; target=&#34;_blank&#34;&gt;Plugin Framework for Java (PF4J)&lt;/a&gt;
 to indicate an &lt;em&gt;extension point&lt;/em&gt;
interface to a service. You can create a plugin that implements the methods declared in an extension point. Creating a
plugin based on an extension point has a number of advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&amp;rsquo;s the easiest - use the &lt;code&gt;@Extension&lt;/code&gt; annotation and implement the methods declared in your chosen extension point&lt;/li&gt;
&lt;li&gt;Spinnaker loads the plugin in an isolated classpath&lt;/li&gt;
&lt;li&gt;It has the least amount of maintenance work&lt;/li&gt;
&lt;li&gt;Updates to Spinnaker are not likely to break your plugin&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;finding-an-extension-point&#34;&gt;Finding an extension point&lt;/h2&gt;
&lt;p&gt;An extension point is an interface that extends &lt;code&gt;org.pf4j.ExtensionPoint&lt;/code&gt; and is located in the &lt;code&gt;api&lt;/code&gt; module of a
service. Spinnaker exposes the following extension points:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Orca&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href=&#34;https://github.com/spinnaker/spinnaker/blob/main/orca/orca-api/src/sample/java/stages/simple/SimpleStageSample.java&#34; target=&#34;_blank&#34;&gt;SimpleStage&lt;/a&gt;

for creating a custom pipeline stage&lt;/li&gt;
&lt;li&gt;[PreconfiguredJobConfigurationProvider]https://github.com/spinnaker/spinnaker/blob/main/orca/orca-api/src/main/java/com/netflix/spinnaker/orca/api/preconfigured/jobs/PreconfiguredJobConfigurationProvider.java)
for provisioning preconfigured Job stages&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Echo&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href=&#34;https://github.com/spinnaker/spinnaker/blob/main/echo/echo-api/src/main/java/com/netflix/spinnaker/echo/api/events/EventListener.java&#34; target=&#34;_blank&#34;&gt;EventListener&lt;/a&gt;

for processing events posted into Echo&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Look through the code or ask in
the 
&lt;a href=&#34;https://join.slack.com/t/spinnakerteam/shared_invite/zt-3f4dqg66a-hX~tWeWPL3Sfnj3F8Ie2xg/&#34; target=&#34;_blank&#34;&gt;Spinnaker Slack&lt;/a&gt;
 to find
extension points not listed here.&lt;/p&gt;
&lt;h2 id=&#34;example-extensionpoint-plugin&#34;&gt;Example ExtensionPoint plugin&lt;/h2&gt;
&lt;p&gt;The 
&lt;a href=&#34;https://github.com/spinnaker-plugin-examples/pf4jStagePlugin&#34; target=&#34;_blank&#34;&gt;pf4jStagePlugin&lt;/a&gt;
 creates a custom pipeline stage that
waits a specified number of seconds before signaling success. Consult
the 
&lt;a href=&#34;https://deploy-preview-629--spinnaker-io.netlify.app/docs/community/contributing/code/developer-guides/plugin-creators/stage-plugin-walkthrough/&#34;&gt;Pipeline Stage Plugin Walkthrough&lt;/a&gt;

for a detailed explanation of this plugin.&lt;/p&gt;
&lt;h2 id=&#34;interface-non-extensionpoint-plugins&#34;&gt;Interface (Non-ExtensionPoint) plugins&lt;/h2&gt;
&lt;p&gt;The second way you can create a plugin is to implement a regular Java interface that you find in a service. Your plugin
uses the PF4J &lt;code&gt;@Extension&lt;/code&gt; annotation but does not extend &lt;code&gt;org.pf4j.ExtensionPoint&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spinnaker loads the plugin in an isolated classpath&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Disadvantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Requires a moderate knowledge of Spinnaker&amp;rsquo;s architecture and code&lt;/li&gt;
&lt;li&gt;Plugin can break if the service&amp;rsquo;s interface changes&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;example-interface-plugin&#34;&gt;Example Interface plugin&lt;/h3&gt;
&lt;p&gt;The 
&lt;a href=&#34;https://github.com/spinnaker-plugin-examples/pf4jPluginWithoutExtensionPoint&#34; target=&#34;_blank&#34;&gt;pf4jPluginWithoutExtensionPoint&lt;/a&gt;

plugin extends the functionality of
Kork&amp;rsquo;s 
&lt;a href=&#34;https://github.com/spinnaker/spinnaker/blob/main/kork/kork-secrets/src/main/java/com/netflix/spinnaker/kork/secrets/SecretEngine.java&#34; target=&#34;_blank&#34;&gt;SecretEngine&lt;/a&gt;
.
SecretEngine is a regular Java interface that does not import any PF4J classes. pf4jPluginWithoutExtensionPoint&amp;rsquo;s
SillySecretEngine implements SecretEngine and uses the &lt;code&gt;@Extension&lt;/code&gt; annotation to identify itself as a PF4J plugin. See
the plugin project&amp;rsquo;s 
&lt;a href=&#34;https://github.com/spinnaker-plugin-examples/pf4jPluginWithoutExtensionPoint&#34; target=&#34;_blank&#34;&gt;README&lt;/a&gt;
 and code for
details on how this plugin works.&lt;/p&gt;
&lt;h2 id=&#34;spring-plugins&#34;&gt;Spring plugins&lt;/h2&gt;
&lt;p&gt;When you can&amp;rsquo;t find an &lt;code&gt;org.pf4j.ExtensionPoint&lt;/code&gt; to use or a Java interface to implement, you can create a plugin using
Spring. This is should be done as a last resort, since the disadvantages outweigh the advantages.&lt;/p&gt;
&lt;p&gt;Advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Full control&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Disadvantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Requires an expert knowledge of Spinnaker&amp;rsquo;s architecture and codebase&lt;/li&gt;
&lt;li&gt;Requires working knowledge of Spring&lt;/li&gt;
&lt;li&gt;High maintenance; plugin can break when Spinnaker dependencies and functionality change&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;example-spring-plugin&#34;&gt;Example Spring plugin&lt;/h3&gt;
&lt;p&gt;The Spring Example Plugin does not use a PF4J extension point or dependencies. It uses Spring components and was created
to test various use cases. See the 
&lt;a href=&#34;https://github.com/spinnaker-plugin-examples/springExamplePlugin&#34; target=&#34;_blank&#34;&gt;project&lt;/a&gt;
 for
details.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Docs: Plugin Project Configuration</title>
      <link>https://deploy-preview-629--spinnaker-io.netlify.app/docs/community/contributing/code/developer-guides/plugin-creators/project-config/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://deploy-preview-629--spinnaker-io.netlify.app/docs/community/contributing/code/developer-guides/plugin-creators/project-config/</guid>
      <description>
        
        
        &lt;div class=&#34;alert alert-warning&#34; role=&#34;alert&#34;&gt;

  &lt;p&gt;
	 
    The contents of this page refer to alpha features in Spinnaker
    1.19.4.
	 
  &lt;/p&gt;
  &lt;p&gt;
    This means we are working on their stability and usability, as well as
    possibly adding or changing features. Expect rough edges, and file &lt;a
    href=&#34;https://github.com/spinnaker/spinnaker/issues&#34;&gt;issues&lt;/a&gt; as needed.
  &lt;/p&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This guide is a work in progress. Help us improve the content by submitting a pull request!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;project-configuration&#34;&gt;Project Configuration&lt;/h2&gt;
&lt;p&gt;Keep the following recommendations and requirements in mind: - We recommend
making the project a Gradle project. There is Gradle tooling to support plugin
development. - You can add any dependencies you need to make your plugin
successful.. &lt;em&gt;TODO-CF&lt;/em&gt; this is almost certainly a lie when it comes to things
that conflict with the dependencies in the compileOnly scope&amp;hellip;&lt;/p&gt;
&lt;h3 id=&#34;gradle-configuration&#34;&gt;Gradle configuration&lt;/h3&gt;
&lt;p&gt;Some important aspects of your project&amp;rsquo;s gradle build:&lt;/p&gt;
&lt;h3 id=&#34;gradleproperties&#34;&gt;gradle.properties&lt;/h3&gt;
&lt;p&gt;Organizing these values into gradle.properties is optional, but these versions are very much mandatory.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;spinnakerGradleVersion=7.5.2
pf4jVersion=3.2.0
korkVersion=7.30.0
orcaVersion=8.2.0
version=1.2.3
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that managing the version of your plugin here (&lt;code&gt;version=1.2.3&lt;/code&gt;) requires
modifying this file for each release. There are fancier ways (the example
project uses a git tag strategy and additional gradle plugins) but those are
outside the scope of this documentation.&lt;/p&gt;
&lt;h3 id=&#34;top-level-buildgradle&#34;&gt;Top-level build.gradle&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// (1):
buildscript {
  repositories {
    maven { url &amp;#34;https://dl.bintray.com/spinnaker/gradle/&amp;#34; }
  }
  dependencies {
    classpath(&amp;#34;com.netflix.spinnaker.gradle:spinnaker-extensions:$spinnakerGradleVersion&amp;#34;)
  }
}

// (2):
plugins {
  //... other plugins you might be using
  id(&amp;#34;com.moowork.node&amp;#34;).version(&amp;#34;1.3.1&amp;#34;).apply(false)
}

// (3):
apply plugin: &amp;#34;io.spinnaker.plugin.bundler&amp;#34;
spinnakerBundle {
  pluginId = &amp;#34;Armory.RandomWaitPlugin&amp;#34;
  description = &amp;#34;An example of a PF4J based plugin, that provides a new stage.&amp;#34;
  provider = &amp;#34;https://github.com/spinnaker-plugin-examples&amp;#34;
  version = rootProject.version
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Notes:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;buildscript&lt;/code&gt; block brings in the spinnaker-extensions gradle tooling, which includes plugins for creating a bundle, ui-extension, and service-extension.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;plugins&lt;/code&gt; block imports the node plugin but does not apply it. This plugin is needed by the ui-extension to build the us assets.&lt;/li&gt;
&lt;li&gt;At the top level we apply the &lt;code&gt;io.spinnaker.plugin.bundler&lt;/code&gt; to define the name of the plugin bundle this project is producing along with bundle metadata.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;ui-extension-buildgradle&#34;&gt;UI-extension build.gradle&lt;/h3&gt;
&lt;p&gt;To build a UI extension with the base conventions, the only thing necessary is
applying the &lt;code&gt;io.spinnaker.plugin.ui-extension&lt;/code&gt; plugin.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TODO&lt;/strong&gt; document what the ui-extension plugin supplies / supports. Maybe some of that is below?&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;apply plugin: &amp;#34;io.spinnaker.plugin.ui-extension&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;service-extension-buildgradle&#34;&gt;Service extension build.gradle&lt;/h3&gt;
&lt;p&gt;Here are the basic things your service-extension gradle build needs. Note that
the example project includes a bunch of additional configuration to configure
kotlin for the plugin. The configuration below strips that out to just include
the basic gradle configuration necessary for a JVM plugin, if you wish to
develop plugins in kotlin refer to the gradle build from the example project.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// (1):
apply plugin: &amp;#34;io.spinnaker.plugin.service-extension&amp;#34;
apply plugin: &amp;#34;maven-publish&amp;#34;

// (2):
sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
  // (3):
  annotationProcessor(&amp;#34;org.pf4j:pf4j:$pf4jVersion)

  // (4):
  compileOnly(&amp;#34;com.netflix.spinnaker.kork:kork-plugins-api:$korkVersion&amp;#34;)
  compileOnly(&amp;#34;com.netflix.spinnaker.orca:orca-api:$orcaVersion&amp;#34;)

  // (5):
  // implementation(&amp;#34;com.something.my.plugin.needs:dependency:version&amp;#34;)
}

// (6):
spinnakerPlugin {
  serviceName = &amp;#34;orca&amp;#34;
  pluginClass = &amp;#34;io.armory.plugin.stage.wait.random.RandomWaitPlugin&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Notes:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;io.spinnaker.plugin.service-extension&lt;/code&gt; and &lt;code&gt;maven-publish&lt;/code&gt; are used for plugin bundling
and metadata generation.&lt;/li&gt;
&lt;li&gt;Spinnaker services require JDK 1.8 compatible bytecode.&lt;/li&gt;
&lt;li&gt;Enable pf4j annotation processing to build plugin extension metadata&lt;/li&gt;
&lt;li&gt;The plugin APIs are marked as &lt;code&gt;compileOnly&lt;/code&gt; so as to not include them in the resulting plugin jar. These dependencies are loaded from the service&amp;rsquo;s classloader, not the plugin classloader.&lt;/li&gt;
&lt;li&gt;You can include additional dependencies that your plugin requires. These dependencies will be bundled into the plugin and in a private classloader for the plugin.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;spinnakerPlugin&lt;/code&gt; block is used to generate plugin and bundle metadata. The &lt;code&gt;serviceName&lt;/code&gt; is the spinnaker service this plugin extends (and should match the service api dependency brought in in &lt;code&gt;(4)&lt;/code&gt; above).&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;intellij-configuration&#34;&gt;IntelliJ Configuration&lt;/h2&gt;
&lt;p&gt;With the gradle configuraton above, you should be able to import your plugin into IntelliJ as a project.&lt;/p&gt;
&lt;h3 id=&#34;ui-extension-ide-configuration&#34;&gt;UI-extension IDE configuration&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Help us improve this section by submitting a pull request!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;service-extension-ide-configuration&#34;&gt;Service-extension IDE configuration&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Help us improve this section by submitting a pull request!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&#34;plugin-debugging-in-a-local-service&#34;&gt;Plugin debugging in a local service&lt;/h4&gt;
&lt;p&gt;If you have a local instance of a host service (in the pf4jStagePlugin example this is &lt;code&gt;orca&lt;/code&gt;) running, you can link your plugin into that service with a &lt;code&gt;plugin-ref&lt;/code&gt; that points to your local development workspace.&lt;/p&gt;
&lt;p&gt;There are two options, depending on how you run the host service:&lt;/p&gt;
&lt;h5 id=&#34;running-the-host-service-in-the-ide&#34;&gt;Running the host service in the IDE&lt;/h5&gt;
&lt;p&gt;If you have the ability to run a service locally in IntelliJ (the configuration for that is outside the scope of this document), then you can add configure your IntelliJ project with both the host service and your plugin in the same workspace.&lt;/p&gt;
&lt;p&gt;Open the IntelliJ project you have configured for the host service, click the &lt;code&gt;+&lt;/code&gt; button on the Gradle tab, and navigate to your plugin project&amp;rsquo;s build.gradle. This will include the plugin project in the workspace.&lt;/p&gt;
&lt;h5 id=&#34;attaching-to-a-remote-jvm-process-for-the-host-service&#34;&gt;Attaching to a remote JVM process for the host service&lt;/h5&gt;
&lt;p&gt;You can debug a service extension in the IDE, provided you have an instance of
the host service to attach a debugger to, and that the host service shares a
filesystem with your plugin development workspace.&lt;/p&gt;
&lt;p&gt;In your plugin project in IntelliJ, in the run configurations, add a configuration of type Remote.&lt;/p&gt;
&lt;p&gt;Select &amp;ldquo;Attach to a remote JVM&amp;rdquo;&lt;/p&gt;
&lt;p&gt;In use module classpath, select the main classpath from the service-extension plugin (helloworld-orca.main) in the example project.&lt;/p&gt;
&lt;p&gt;Copy the command-line arguments for the JVM from this dialog, and ensure however you are launching the host service locally that it gets those JVM arguments.&lt;/p&gt;
&lt;h4 id=&#34;linking-the-plugin-ref-to-the-host-service&#34;&gt;Linking the plugin-ref to the host service&lt;/h4&gt;
&lt;p&gt;When you build your plugin project with gradle, it will produce a &lt;code&gt;.plugin-ref&lt;/code&gt; file in the build/ directory for the plugin. You only need to do this once initially, and if your plugin dependencies are changed to regenerate it.&lt;/p&gt;
&lt;p&gt;Once you have that file generated, navigate to the &lt;code&gt;plugins&lt;/code&gt; directory for the host service, wherever you are running it. You can then create a symlink to the &lt;code&gt;.plugin-ref&lt;/code&gt; file, and this will cause the host service to see your plugin and load the classes from your development workspace when the host service starts up.&lt;/p&gt;
&lt;p&gt;For the example project:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cd /path/to/orca/plugins
$ ln -sf /path/to/dev/workspaces/spinnaker-plugin-helloworld/helloworld-orca/build/orca.plugin-ref
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, when you attach your debugger to the host services, any breakpoints in your plugin code will be hit and you can step through them in your IDE, evaluate expressions, etc.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Docs: Pipeline Stage Plugin Walkthrough</title>
      <link>https://deploy-preview-629--spinnaker-io.netlify.app/docs/community/contributing/code/developer-guides/plugin-creators/stage-plugin-walkthrough/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://deploy-preview-629--spinnaker-io.netlify.app/docs/community/contributing/code/developer-guides/plugin-creators/stage-plugin-walkthrough/</guid>
      <description>
        
        
        &lt;div class=&#34;alert alert-warning&#34; role=&#34;alert&#34;&gt;

  &lt;p&gt;
	 
    The contents of this page refer to alpha features in Spinnaker
    1.19.4.
	 
  &lt;/p&gt;
  &lt;p&gt;
    This means we are working on their stability and usability, as well as
    possibly adding or changing features. Expect rough edges, and file &lt;a
    href=&#34;https://github.com/spinnaker/spinnaker/issues&#34;&gt;issues&lt;/a&gt; as needed.
  &lt;/p&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This guide is a work in progress. Help us improve the content by submitting a pull request!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;requirements&#34;&gt;Requirements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;You have read the 
&lt;a href=&#34;https://deploy-preview-629--spinnaker-io.netlify.app/docs/v1/guides/developer/plugin-creators/overview/&#34;&gt;Plugin Creators Guide Overview&lt;/a&gt;
.&lt;/li&gt;
&lt;li&gt;
&lt;a href=&#34;https://gradle.org/&#34; target=&#34;_blank&#34;&gt;Gradle&lt;/a&gt;
 and 
&lt;a href=&#34;https://classic.yarnpkg.com/en/&#34; target=&#34;_blank&#34;&gt;Yarn&lt;/a&gt;
 for building the plugin locally&lt;/li&gt;
&lt;li&gt;IntelliJ IDEA, Orca branch &lt;code&gt;release-1.19.x&lt;/code&gt; and Deck branch  for local testing&lt;/li&gt;
&lt;li&gt;Spinnaker current release for deploying the pf4jStagePlugin 1.0.16&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;pf4jstageplugin-plugin&#34;&gt;pf4jStagePlugin plugin&lt;/h2&gt;
&lt;p&gt;The 
&lt;a href=&#34;https://github.com/spinnaker-plugin-examples/pf4jStagePlugin&#34; target=&#34;_blank&#34;&gt;pf4jStagePlugin&lt;/a&gt;
 creates a custom pipeline stage that waits a number of seconds before signaling success. The plugin consists of a &lt;code&gt;random-wait-orca&lt;/code&gt; 
&lt;a href=&#34;https://kotlinlang.org/docs/reference/&#34; target=&#34;_blank&#34;&gt;Kotlin&lt;/a&gt;
 server component and a &lt;code&gt;random-wait-deck&lt;/code&gt; 
&lt;a href=&#34;https://reactjs.org/&#34; target=&#34;_blank&#34;&gt;React&lt;/a&gt;
 UI component.&lt;/p&gt;
&lt;p&gt;This is a very simplistic plugin for educational purposes only. You can use this plugin as a starting point to create a custom pipeline stage.&lt;/p&gt;
&lt;h3 id=&#34;random-wait-orca&#34;&gt;&lt;code&gt;random-wait-orca&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This component implements the 
&lt;a href=&#34;https://github.com/spinnaker/orca/blob/ab89a0d7f847205ccd62e70f8a714040a8621ee7/orca-api/src/main/java/com/netflix/spinnaker/orca/api/SimpleStage.java&#34; target=&#34;_blank&#34;&gt;SimpleStage&lt;/a&gt;
 PF4J extension point in Orca and  consists of five classes in the &lt;code&gt;io.armory.plugin.state.wait.random&lt;/code&gt; package:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Context.kt&lt;/code&gt;: a data class that stores the &lt;code&gt;maxWaitTime&lt;/code&gt; value; &lt;code&gt;SimpleStage&lt;/code&gt; uses &lt;code&gt;Context&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Output.kt&lt;/code&gt;: a data class that stores the &lt;code&gt;timeToWait&lt;/code&gt; getValue; this data is returned to the extension point implementation and can be used in later stages&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RandomWaitConfig.kt&lt;/code&gt;: a data class with the &lt;code&gt;@ExtensionConfiguration&lt;/code&gt; tag; key-value pairs in this class map to the plugin&amp;rsquo;s configuration&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RandomWaitInput.kt&lt;/code&gt;: a data class that contains the key-values pairs that we care about from the Context map&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RandomWaitPlugin.kt&lt;/code&gt;: this is the plugin&amp;rsquo;s main class; implements &lt;code&gt;SimpleStage&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Watch a 
&lt;a href=&#34;https://youtu.be/b7BmMY1kR10&#34; target=&#34;_blank&#34;&gt;video walkthrough&lt;/a&gt;
 and read 
&lt;a href=&#34;https://github.com/spinnaker-plugin-examples/pf4jStagePlugin/tree/master/random-wait-orca/src/main/kotlin/io/armory/plugin/stage/wait/random&#34; target=&#34;_blank&#34;&gt;code comments&lt;/a&gt;
 for more information.&lt;/p&gt;
&lt;h3 id=&#34;random-wait-deck&#34;&gt;&lt;code&gt;random-wait-deck&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This component uses the 
&lt;a href=&#34;https://rollupjs.org/guide/en/#plugins-overview&#34; target=&#34;_blank&#34;&gt;&amp;lt;code&amp;gt;rollup.js&amp;lt;/code&amp;gt;&lt;/a&gt;
 plugin library to create a UI widget for Deck.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;rollup.config.js&lt;/code&gt;: configuration for building the JavaScript application&lt;/li&gt;
&lt;li&gt;&lt;code&gt;package.json&lt;/code&gt;: defines dependencies&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RandomWaitStage.tsx&lt;/code&gt;: defines the custom pipeline stage; renders UI output&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RandomWaitStageIndex.ts&lt;/code&gt;: exports the name and custom stages&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Watch a 
&lt;a href=&#34;https://youtu.be/u9NVlG58NYo&#34; target=&#34;_blank&#34;&gt;video walkthrough&lt;/a&gt;
 and read 
&lt;a href=&#34;https://github.com/spinnaker-plugin-examples/pf4jStagePlugin/tree/master/random-wait-deck/src&#34; target=&#34;_blank&#34;&gt;code comments&lt;/a&gt;
 for details.&lt;/p&gt;
&lt;h2 id=&#34;building-the-release-bundle&#34;&gt;Building the release bundle&lt;/h2&gt;
&lt;p&gt;After you 
&lt;a href=&#34;https://github.com/spinnaker-plugin-examples/pf4jStagePlugin/archive/v1.0.16.tar.gz&#34; target=&#34;_blank&#34;&gt;download&lt;/a&gt;
 the 1.0.16 source code, build the release bundle.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#204a87&#34;&gt;cd&lt;/span&gt; pf4jStagePlugin
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./gradlew releaseBundle
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This command creates the &lt;code&gt;pf4jStagePlugin-1.0.16.zip&lt;/code&gt; in &lt;code&gt;pf4jStagePlugin/build/distributions&lt;/code&gt;. Locate the &lt;code&gt;MANIFEST.MF&lt;/code&gt; in pf4jStagePlugin-1.0.16.zip -&amp;gt; orca.zip -&amp;gt; classes -&amp;gt; META-INF&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Manifest-Version: 1.0
Plugin-Description: An example of a PF4J based plugin, that provides a
  new stage.
Plugin-Id: Armory.RandomWaitPlugin
Plugin-Provider: https://github.com/claymccoy
Plugin-Version: unspecified
Plugin-Class: io.armory.plugin.stage.wait.random.RandomWaitPlugin
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;debugging-random-wait-orca-in-orca-locally&#34;&gt;Debugging &lt;code&gt;random-wait-orca&lt;/code&gt; in Orca locally&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Help us improve this section by submitting a pull request!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Ensure your development environment is set up to run Orca locally.&lt;/li&gt;
&lt;li&gt;Clone Orca branch release-1.19.x&lt;/li&gt;
&lt;li&gt;Import into IntelliJ&lt;/li&gt;
&lt;li&gt;Follow the steps in the 
&lt;a href=&#34;https://github.com/spinnaker-plugin-examples/pf4jStagePlugin#debugging&#34; target=&#34;_blank&#34;&gt;Debugging&lt;/a&gt;
 section of the pf4jStagePlugin README.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;debugging-random-wait-deck-in-deck-locally&#34;&gt;Debugging &lt;code&gt;random-wait-deck&lt;/code&gt; in Deck locally&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Help us improve this section by submitting a pull request!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Obtain the Deck release-1.19.x branch.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a folder called &lt;code&gt;plugins&lt;/code&gt; in the &lt;code&gt;deck&lt;/code&gt; directory. Copy &lt;code&gt;RandomWaitStageIndex.js&lt;/code&gt; to the new directory.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add to  &lt;code&gt;deck\plugin-manifest.json&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#4e9a06&#34;&gt;&amp;#34;Armory.RandomWaitPlugin&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#4e9a06&#34;&gt;&amp;#34;1.0.16&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#4e9a06&#34;&gt;&amp;#34;/plugins/RandomWaitStageIndex.js&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Start Deck and navigate to the Pipeline creation screen. Verify that &lt;code&gt;Random Wait&lt;/code&gt; is an option in the Stage drop-down.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;plugin-build-and-configuration-files&#34;&gt;Plugin build and configuration files&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Help us improve this section by submitting a pull request!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;build.gradle&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;spinnakerBundle&lt;/code&gt; section&lt;/li&gt;
&lt;li&gt;&lt;code&gt;subprojects&lt;/code&gt; section&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;random-wait-orca&lt;/code&gt; subproject, &lt;code&gt;random-wait-orca.gradle&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;spinnakerPlugin&lt;/code&gt; section&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&#34;text-align:left&#34;&gt;If you change&lt;/th&gt;
&lt;th style=&#34;text-align:left&#34;&gt;Update values in these files&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;project name&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;code&gt;settings.gradle&lt;/code&gt;, &lt;code&gt;build.gradle&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;subproject name&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;code&gt;settings.gradle&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

      </description>
    </item>
    
  </channel>
</rss>
