<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Spinnaker – Development Guides</title>
    <link>https://deploy-preview-629--spinnaker-io.netlify.app/docs/guides/developer/</link>
    <description>Recent content in Development Guides on Spinnaker</description>
    <generator>Hugo -- gohugo.io</generator>
    
	  <atom:link href="https://deploy-preview-629--spinnaker-io.netlify.app/docs/guides/developer/index.xml" rel="self" type="application/rss+xml" />
    
    
      
        
      
    
    
    <item>
      <title>Docs: Extending Spinnaker</title>
      <link>https://deploy-preview-629--spinnaker-io.netlify.app/docs/guides/developer/extending/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://deploy-preview-629--spinnaker-io.netlify.app/docs/guides/developer/extending/</guid>
      <description>
        
        
        
      </description>
    </item>
    
    <item>
      <title>Docs: Plugin Creator Guide</title>
      <link>https://deploy-preview-629--spinnaker-io.netlify.app/docs/guides/developer/plugin-creator/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://deploy-preview-629--spinnaker-io.netlify.app/docs/guides/developer/plugin-creator/</guid>
      <description>
        
        
        &lt;h2 id=&#34;what-plugins-do&#34;&gt;What plugins do&lt;/h2&gt;
&lt;p&gt;Plugins enable operators 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;p&gt;The goal of this guide is to help with the core services side of plugin
development: understand how plugins work and how to create new extension
points for plugin developers to use.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Plugins are available in Spinnaker &lt;code&gt;1.20.6+&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;why-there-is-a-plugin-framework&#34;&gt;Why there is a plugin framework&lt;/h2&gt;
&lt;p&gt;Spinnaker was originally written with extensibility in mind. Netflix
wrote huge amounts of custom code atop open source Spinnaker, decorating existing
functionality or replacing entire areas to suit their needs. This was done either
by consuming the open source projects as libraries and laying custom code on top, or
wiring it together via Spring configuration (or something else in Deck-land).&lt;/p&gt;
&lt;p&gt;This is well and good, but by making the method of extension application
configuration classes, the contract for extensions is essentially the entire
codebase. Good for getting things done fast, but bad for creating clear domain
contracts, which leads to a mixing of core service code and integrations. Over
time, this extension pattern manifests itself in making the core services
heavy and difficult to maintain.&lt;/p&gt;
&lt;p&gt;Realizing this, Netflix started an early initiative called 
&lt;a href=&#34;https://docs.google.com/document/d/1cgKBdT5xVFvMwut7Wji_-bC_12GoQtyZ2MQ958LDcOY/edit?usp=sharing&#34; target=&#34;_blank&#34;&gt;Lean Core, Fat
Ecosystem&lt;/a&gt;
.
Plugins were the first major manifestation of this initiative: To take the
already-built functionality in Spinnaker and start breaking it out into
composable, separately distributable binaries.&lt;/p&gt;
&lt;h2 id=&#34;terminology&#34;&gt;Terminology&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Extension Point&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;An interface defined by one of the Spinnaker services for adding specific
functionality.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extension&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;An implementation of an &lt;strong&gt;Extension Point&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plugin&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;A collection of &lt;strong&gt;Extensions&lt;/strong&gt; for a single Spinnaker service.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bundle&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;A collection of related &lt;strong&gt;Plugins&lt;/strong&gt; that span Spinnaker services making
up a complete feature.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SDK&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Libraries offered by the plugin framework and Spinnaker services to
assist common use cases.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TCK&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Test harnesses and utilities to help plugin developers assert Extension
functionality.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;what-should-be-an-extension-point&#34;&gt;What should be an Extension Point&lt;/h2&gt;
&lt;p&gt;An Extension Point should be made at an intersection between a service&amp;rsquo;s core
functionality and what it considers an integration. An integration is value
added to a service, but not value that impacts the core functionality offerings
of the service: The differentiator is that a service cannot function without
its core functionality, whereas an integration, as critical as it may be for a
particular configuration of that service, is ultimately optional.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s take 
&lt;a href=&#34;https://github.com/spinnaker/orca&#34; target=&#34;_blank&#34;&gt;Orca&lt;/a&gt;
 as an example: Individual
Pipeline Stages and SpEL Functions are two integrations. Service code that
enables the use of Stages, such as the pipeline engine itself, is a core
feature that isn&amp;rsquo;t an extension point.&lt;/p&gt;
&lt;p&gt;When we look at a service under the lens of &lt;em&gt;Lean Core, Fat Ecosystem&lt;/em&gt;, we want
to have stable core services that only change for the purpose of enabling or
fixing core functionality. Most service deployments for Spinnaker today are for
integrations.&lt;/p&gt;
&lt;p&gt;So, for a Spinnaker service, if there is an integration, it should be enabled
by one or more Extension Points. Going back to the Orca Stage example, a Stage
is actually comprised of multiple Extension Points: There is &lt;code&gt;Task&lt;/code&gt;, which is
responsible for performing a small, discrete action, as well as
&lt;code&gt;StageDefinitionBuilder&lt;/code&gt;, which is responsible for defining how various &lt;code&gt;Task&lt;/code&gt;
classes interact with each other, and when.&lt;/p&gt;
&lt;p&gt;Extension Points should be small and composable and, when used in concert with
each other, enable larger value for Spinnaker than the sum of its parts.&lt;/p&gt;
&lt;h2 id=&#34;plugin-types&#34;&gt;Plugin types&lt;/h2&gt;
&lt;h3 id=&#34;frontend-deck-plugins&#34;&gt;Frontend (Deck) plugins&lt;/h3&gt;
&lt;p&gt;Frontend plugins provide a way to change the behavior of Deck, Spinnaker&amp;rsquo;s UI
service. You can add configuration and validation for new stages provided by
Orca plugins, override existing components with your own implementation, or add
new Kubernetes &lt;code&gt;kind&lt;/code&gt; definitions for custom resources in your environment. Spinnaker loads plugins at runtime through Gate.&lt;/p&gt;
&lt;p&gt;You can write plugins in any JavaScript-compatible language, but the development tooling is designed for JavaScript and TypeScript.&lt;/p&gt;
&lt;p&gt;The following are examples of Deck features that you can override:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href=&#34;https://github.com/spinnaker/deck/blob/master/packages/core/src/application/ApplicationIcon.tsx&#34; target=&#34;_blank&#34;&gt;ApplicationIcon&lt;/a&gt;
, replace the icon used to represent applications in Deck.&lt;/li&gt;
&lt;li&gt;
&lt;a href=&#34;https://github.com/spinnaker/deck/blob/master/packages/core/src/serverGroup/ServerGroupHeader.tsx&#34; target=&#34;_blank&#34;&gt;ServerGroupHeader&lt;/a&gt;
, replace how pod status is reported in Deck.&lt;/li&gt;
&lt;li&gt;
&lt;a href=&#34;https://github.com/spinnaker/deck/blob/master/packages/core/src/header/SpinnakerHeader.tsx&#34; target=&#34;_blank&#34;&gt;SpinnakerHeader&lt;/a&gt;
, replace the top navigation header.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following projects demonstrate adding new stages to Spinnaker:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href=&#34;https://github.com/spinnaker-plugin-examples/nomadPlugin&#34; target=&#34;_blank&#34;&gt;nomadPlugin&lt;/a&gt;
, adding a Nomad provider to Spinnaker.&lt;/li&gt;
&lt;li&gt;
&lt;a href=&#34;https://github.com/spinnaker-plugin-examples/pf4jStagePlugin&#34; target=&#34;_blank&#34;&gt;pf4jStagePlugin&lt;/a&gt;
, adding a sample random wait stage to Spinnaker.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See the 
&lt;a href=&#34;https://deploy-preview-629--spinnaker-io.netlify.app/docs/guides/developer/plugin-creator/plugin-frontend/&#34;&gt;Frontend Plugin Development&lt;/a&gt;

guide for more information.&lt;/p&gt;
&lt;h3 id=&#34;extensionpoint-plugins&#34;&gt;ExtensionPoint plugins&lt;/h3&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;h4 id=&#34;finding-an-extension-point&#34;&gt;Finding an extension point&lt;/h4&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. The following list provides a
sample of what these extension points look like in Orca and Echo:&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/orca/blob/master/orca-api/src/main/java/com/netflix/spinnaker/orca/api/pipeline/graph/StageDefinitionBuilder.java&#34; target=&#34;_blank&#34;&gt;StageDefinitionBuilder&lt;/a&gt;
 for creating a custom pipeline stage&lt;/li&gt;
&lt;li&gt;
&lt;a href=&#34;https://github.com/spinnaker/orca/blob/master/orca-api/src/main/java/com/netflix/spinnaker/orca/api/pipeline/Task.java&#34; target=&#34;_blank&#34;&gt;Task&lt;/a&gt;
 for creating a custom pipeline task to use in a custom pipeline stage&lt;/li&gt;
&lt;li&gt;
&lt;a href=&#34;https://github.com/spinnaker/orca/blob/master/orca-api/src/main/java/com/netflix/spinnaker/orca/api/preconfigured/jobs/PreconfiguredJobConfigurationProvider.java&#34; target=&#34;_blank&#34;&gt;PreconfiguredJobConfigurationProvider&lt;/a&gt;
 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/echo/blob/master/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;h4 id=&#34;example-extensionpoint-plugin&#34;&gt;Example ExtensionPoint Plugin&lt;/h4&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/guides/developer/plugin-creator/testing/plugin-deck-test/&#34;&gt;Test a Pipeline Stage Plugin&lt;/a&gt;
 guide for how to test this plugin using a local Spinnaker environment.&lt;/p&gt;
&lt;h3 id=&#34;interface-plugins&#34;&gt;Interface Plugins&lt;/h3&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;h4 id=&#34;example-interface-plugin&#34;&gt;Example Interface Plugin&lt;/h4&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/kork/blob/5c5bf12a54ca840b7c6c9f4a57cf3c445ddd910e/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;h3 id=&#34;spring-plugins&#34;&gt;Spring Plugins&lt;/h3&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;h4 id=&#34;example-spring-plugin&#34;&gt;Example Spring Plugin&lt;/h4&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;
&lt;h2 id=&#34;next-steps&#34;&gt;Next steps&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href=&#34;https://deploy-preview-629--spinnaker-io.netlify.app/docs/guides/developer/plugin-creator/plugin-backend/&#34;}&gt;Backend Service Extension Points&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href=&#34;https://deploy-preview-629--spinnaker-io.netlify.app/docs/guides/developer/plugin-creator/plugin-frontend/&#34;}&gt;Frontend Plugin Development&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href=&#34;https://deploy-preview-629--spinnaker-io.netlify.app/docs/guides/developer/plugin-creator/testing/plugin-deck-test/&#34;}&gt;Test a Pipeline Stage Plugin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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