As of CloudBees CD/RO release 23.02.0, the legacy CloudBees plugin manager is deprecated. Any plugin using a legacy model should be migrated to the plugin developer kit (pdk) configuration model to continue support. The following instructions and examples may be used as a blueprint to migrate your plugins to pdk. However, depending on how a project-specific plugin was implemented within your project, it may have different and project-specific steps that require project-specific migration.
Get started
The following are requirements or helpful information to migrate your plugins to pdk:
-
You must have the
pdk
CLI tool installed and configured to migrate plugins from PluginWizard to pdk. If you have not already installed and configuredpdk
, refer to Install pdk CLI. -
When migrating any plugin to pdk, CloudBees strongly suggests to create a new project, copy your source files to it, and perform the migration steps on the copied files. This is to prevent errors that may occur during migration from affecting your source files in a severe manner.
-
You can display many helpful
pdk
command references in your CLI by runningpdk -help
.
Bootstrap plugin project
Using pdk generate workspace
, you can bootstrap a project workspace in the current directory to use to create and migrate plugins. To get started, navigate to the desired directory and run:
pdk generate workspace
After doing so, you are prompted to enter the details for your plugin. The following is a sample output:
Bootstrap sample output
➜ /tmp pdk generate workspace
WARN: Congrats, you are the plugin developer. Fasten the seat belts, I'll show you how deep the rabbit hole goes.
INFO: Writing log to the file /Users/imago/.flowpdf/pdk.log
WARN: You are using EDGE version of flowpdf-cli
INFO: Plugin name is not provided
Please provide a name for the new plugin, e.g. MyPlugin: EC-CloudFoundry
Please provide a workspace type or types.
Available types are rest, reporting or default (default: 'default', separated by a comma or a space):
Please provide the author name (default: none):
Please provide the support URL (default: none):
Please provide the plugin category (default: Utilities):
Please provide the description for the plugin:
Please provide the language for the plugin (default: groovy, available languages are perl, groovy):
WARN: Language is not supported, Groovy will be used by default
INFO: Wrote initial declaration to /private/tmp/EC-CloudFoundry/config/pluginspec.yaml
INFO: Plugin folder is /private/tmp/EC-CloudFoundry
INFO: Wrote flowpdf specification into /private/tmp/EC-CloudFoundry/config/flowpdf.yaml
INFO: Initial plugin spec has been placed into /private/tmp/EC-CloudFoundry/config/pluginspec.yaml
INFO: Go into the plugin directory: 'cd /private/tmp/EC-CloudFoundry'
INFO: Change the plugin spec and run 'pdk generate plugin' from the plugin directory
INFO: Command took 16443 ms to execute
This creates the <plugin-name>
directory with a config
directory that contains a flowpdf.yaml
and skeleton pluginspec.yaml
.
Configure procedure interfaces
In previous plugin configuration models multiple resources were used to configure your plugin’s procedure interfaces. Using pdk, the process of configuring your plugin’s procedure interfaces have been centralized in the pluginspec.yaml
.
Define plugin configuration procedures
In previous plugin development tools, the form.xml
from the CreateConfiguration procedure was used to define the plugin’s configuration in CloudBees CD/RO’s UI. For pdk
, the config/pluginspec.yaml
is the primary file used to configure your plugin. This includes information needed to create configurations for the plugin itself in CloudBees CD/RO’s UI.
To configure your plugin’s Configuration procedure, you must convert the XML used in the flow.xml
to YAML in the pluginspec.yaml
.
The following is an example of a form.xml``s XML being converted to YAML in the `pluginspec.yaml
:
Plugin configuration in XML example
<!--
Copyright 2016 CloudBees, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<editor>
<formElement>
<type>entry</type>
<label>Configuration:</label>
<property>config</property>
<required>1</required>
<documentation>Unique name for the plugin configuration.</documentation>
</formElement>
<formElement>
<type>entry</type>
<label>Description:</label>
<property>desc</property>
<required>0</required>
<documentation>Description for the plugin configuration.</documentation>
</formElement>
<formElement>
<type>entry</type>
<label>API endpoint:</label>
<property>endpoint</property>
<required>1</required>
<documentation>API endpoint for Cloud Foundry, e.g. api.mycloud.com.</documentation>
</formElement>
<formElement>
<type>entry</type>
<label>Organization:</label>
<property>organization</property>
<required>1</required>
<documentation></documentation>
</formElement>
<formElement>
<type>entry</type>
<label>Space:</label>
<property>space</property>
<required>0</required>
<documentation></documentation>
</formElement>
<formElement>
<type>credential</type>
<label>Username and password:</label>
<property>credential</property>
<required>1</required>
<documentation>Credentials to connect</documentation>
<attachedAsParameterToStep>createAndAttachCredential</attachedAsParameterToStep>
</formElement>
<formElement>
<type>select</type>
<label>Debug level:</label>
<property>logLevel</property>
<required>0</required>
<documentation>Verbosity level of logs</documentation>
<option>
<name>Info</name>
<value>1</value>
</option>
<option>
<name>Debug</name>
<value>2</value>
</option>
<option>
<name>Trace</name>
<value>3</value>
</option>
</formElement>
</editor>
Is converted to:
Plugin configuration in YAML example
# This is a shell used for checking connection shell: 'ec-groovy'
# A script for checking connection will be generated
checkConnection: 'true'
# A set of fields will be added to process debug level in the configuration
hasDebugLevel: true
parameters:
-
name: config
documentation: The name for the created configuration
required: true
type: entry
label: Configuration Name
-
name: desc
documentation: Description for the configuration
required: false
type: entry
label: Description
-
name: endpoint
documentation: Third-party endpoint to connect to.
required: false
type: entry
label: Endpoint
-
name: organization
documentation: Third-party endpoint to connect to.
required: true
type: entry
label: Orgainization
-
name: space
documentation: Third-party endpoint to connect to.
required: true
type: entry
label: Space
-
name: credential
documentation: A sample credential
required: true
type: credential
label: Credential
# No need to transfer debugLevel or any other parameter that regulates the log level - it is enabled by the hasDebugLevel: true parameter.
Define plugin procedures
In previous plugin development tools, the plugin’s procedures were stored in a combination of the procedure.dsl
and form.xml
. For pdk, the config/pluginspec.yaml
is the primary file used to define your plugin’s procedures. This includes information needed to create configurations for the plugin’s procedures in CloudBees CD/RO’s UI.
To define your plugin’s procedures, you must convert the DSL and XML used in the previous files to YAML in the pluginspec.yaml
.
The following is an example of a procedure.dsl
being converted to YAML in the pluginspec.yaml
:
Procedure configuration in DSL example
import java.io.File
def procName = 'Unmap Route'
procedure procName,
description: 'Remove a url route from an app', {
step 'setup',
subprocedure: 'flowpdk-setup',
errorHandling: 'failProcedure',
exclusiveMode: 'none',
postProcessor: 'postp',
releaseMode: 'none',
shell: 'ec-perl',
timeLimitUnits: 'minutes'
step 'unmapRoute',
command: '''
$[/myProject/scripts/preamble]
def plugin = CFPlugin.build()
plugin.stepUnmapRoute()
''',
errorHandling: 'abortProcedure',
exclusiveMode: 'none',
postProcessor: 'postp',
releaseMode: 'none',
resourceName: '$[grabbedResource]',
shell: 'ec-groovy',
timeLimitUnits: 'minutes'
}
Procedure configuration in YAML
From here, you need the procedure name, description, and code for the step. The name and the description will go to the definition in the pluginspec.yaml
along with the parameters from the form.xml
:
Procedure configuration in YAML example
-
name: Unmap Route
description: Remove a url route from an app
# configuration field will be generated automatically
hasConfig: true
parameters:
-
name: space
documentation: A space provides users with access to a shared location for application development, deployment, and maintenance. Will be taken from the config, if defined.
required: true
type: entry
label: Space
-
name: applicationName
documentation: Application name
required: true
type: entry
label: Application name
-
name: domain
documentation: Domain for the route.
required: true
type: entry
label: Domain name
-
name: hostname
documentation: Hostname for the HTTP route (required for shared domains).
required: false
type: entry
label: Hostname
-
name: path
documentation: Path for the HTTP route.
required: false
type: entry
label: Path
-
name: port
documentation: ''
required: false
type: entry
label: Port
Config parameters are not transferred but now handled by the hasConfig field instead. Later, you will move the content from the command field of the procedure.dsl into the main plugin’s code file.
|
Compose plugin code
Once you have configured your pluginspec.yaml
, to compile the plugin, run:
pdk generate plugin
This generates a <pluginName>.groovy
. In this file, there will be a placeholder for the step code similar to:
<pluginName>.groovy
example
def unmapRoute(StepParameters p, StepResult sr) {
// Use this parameters wrapper for convenient access to your parameters
UnmapRouteParameters sp = UnmapRouteParameters.initParameters(p)
// Calling logger:
log.info p.asMap.get('config')
log.info p.asMap.get('space')
log.info p.asMap.get('applicationName')
log.info p.asMap.get('domain')
log.info p.asMap.get('hostname')
log.info p.asMap.get('path')
log.info p.asMap.get('port')
// Setting job step summary to the config name
sr.setJobStepSummary(p.getParameter('config')?.getValue() ?: 'null')
sr.setReportUrl("Sample Report", 'https://cloudbees.com')
sr.apply()
log.info("step Unmap Route has been finished")
}
For now, the parameters and job summary are generated. This is where your previous command content goes, excluding the $[]
:
def plugin = CFPlugin.build()
plugin.stepUnmapRoute()
Depending on how your code was organized before, some refactoring may be required. However, all CloudBees CD/RO-related calls are now be handled by pdk and all third-party related calls must be handled by another separated module.
Other modules can be placed along the <pluginName>.groovy
, and called by their names, without a package name used. The resulting modules are concatenated and the step’s method are called when the procedure runs.
The following is an example of a plugin that used a legacy REST client to communicate with the CloudBees CD/RO server, which is no longer needed, and a wrapper around the plugin (Cloud Foundry), which may be reused with a slightly different handling of the input parameters.
The dependencies that are declared in form of grapes are to be turned into gradle dependencies and put into the build.gradle
within the plugin’s root folder.
Legacy REST client example
import com.cloudbees.flowpdf.*
/**
* CloudFoundry
*/
class CloudFoundry extends FlowPlugin {
@Override
Map<String, Object> pluginInfo() {
return [
pluginName : '@PLUGIN_KEY@',
pluginVersion : '@PLUGIN_VERSION@',
configFields : ['config'],
configLocations : ['ec_plugin_cfgs'],
defaultConfigValues: [:]
]
}
/** This is a special method for checking connection during configuration creation
*/
def checkConnection(StepParameters p, StepResult sr) {
// Use this pre-defined method to check connection parameters
try {
// Put some checks here
def config = context.configValues
log.info(config)
// Getting parameters:
// log.info config.asMap.get('config')
// log.info config.asMap.get('desc')
// log.info config.asMap.get('endpoint')
// log.info config.asMap.get('credential')
// assert config.getRequiredCredential("credential").secretValue == "secret"
} catch (Throwable e) {
// Set this property to show the error in the UI
sr.setOutcomeProperty("/myJob/configError", e.message + System.lineSeparator() + "Please change the code of checkConnection method to incorporate your own connection checking logic")
sr.apply()
throw e
}
}
// === check connection ends ===
/**
* unmapRoute - Unmap Route/Unmap Route
* Add your code into this method and it will be called when the step runs
* @param config (required: true)
* @param space (required: true)
* @param applicationName (required: true)
* @param domain (required: true)
* @param hostname (required: false)
* @param path (required: false)
* @param port (required: false)
*/
def unmapRoute(StepParameters p, StepResult sr) {
// Use this parameters wrapper for convenient access to your parameters
UnmapRouteParameters sp = UnmapRouteParameters.initParameters(p)
Map params = p.asMap
params.space = context.configValues.getParameter('space').value
cfClient.unmapRoute(params)
sr.setJobStepSummary("The route has been unmapped")
sr.apply()
}
@Lazy
def cfClient = {
new CFClient(
endpoint: context.configValues.getParameter('endpoint').value,
userName: context.configValues.getCredential('credential')?.userName,
password: context.configValues.getCredential('credential').secretValue,
space: context.configValues.getParameter('space')?.value,
organization: context.configValues.getParameter('organization')?.value,
logLevel: context.configValues.getParameter('debugLevel')?.value as int
)
}()
// === step ends ===
}
For more examples of REST PDK development, refer to:
|
Configure plugin UI text
Within your pluginspec.yaml
, you can use the documentation
tag to enter UI content for each field. For example:
parameters:
-
name: space
documentation: A space provides users with access to a shared location for application development, deployment, and maintenance. Will be taken from the config, if defined.
required: true
type: entry
label: Space
You can use both plain text and HTML for UI text. |
Migrating your plugin configurations
After the plugin manager is updated, your plugin configurations based on GWT may not be available to view. To view legacy plugin configurations:
-
Navigate to
. -
Select the Administration link. The Event Logs page displays.
-
Select the Plugins page link. The Plugins Manager page displays.
-
Select the name of the plugin name in the Plugin Label column. The Project Details page displays.
-
Select the Properties tab.
-
In the property
<PluginName>_cfgs
, you can find all configurations for the plugin. -
Note any specific configuration names that need to be migrated. You can also migrate all configurations.
If you are migrating all configurations, you do not need the names. By default, the Migrate configurations service catalog item migrates all configurations when no configuration names are specified.
To migrate the plugin’s configurations, you can use the Migrate configurations service catalog item:
-
Select the Service catalog from the main CloudBees CD/RO navigation header.
-
In the Service catalog, search for Migrate configurations, and select migrate.
-
(Optional) If you have only specific plugin configurations to migrate, enter them in the Configuration names one per line. If you want to migrate all the plugin’s configurations, leave this field blank.
-
Enter the plugin you want to migrate the in Plugin name field.
-
Use the Project name list to select the specific project for which you want to migrate the plugin configuration(s).
-
Select OK to migrate the plugin configurations.