The UrbanCode Application Deployment Framework (UCADF) Core Library provides functionality to automate management of UrbanCode instances and create UCADF packages that support application deployment patterns. This functionality is provided in the form of actions that can be performed in a specified order to achieve a desired outcome (similar to event sourcing).
The UCADF-Core-Plugin and UCADF-Store projects provide related information.
UrbanCode provides an extensive set of both documented, and undocumented RESTful APIs. The behavior of these APIs can change with each UrbanCode release as the product functionality is changed or new functionality is added.
The UCADF actions provide an abstraction/isolation layer that has the logic to perform a given action, e.g. create a component, predictably across versions while being backward, and forward compatible.
While UrbanCode may provide an extensive set of RESTful APIs, many UrbanCode administrators are more geared toward infrastructure support than development and as such may not have the knowledge, nor desire to write large amounts of code to use the APIs to automate a set of complex UrbanCode administration tasks.
The UCADF provides actions as a DSL that allows administrators to define a set of actions to perform using a YAML syntax.
The UrbanCode UI has steps that allow administrators to create processes to manage UrbanCode entities, e.g. create an application and set the application security, create a component and set the component security, create resources for the applications/components and set the security, create environments and set the security, etc. However, these processes can be time-consuming to create and may take a long time to run when managing a lot of entities as each step runs separately on an agent.
The UCADF provides the ability to define a large set of complex task actions as YAML and run those as one plugin step.
In larger companies there can be a need to have multiple UrbanCode instances which increases the need for automation to manage those instances.
The UCADF provides actions to automate many of the tasks that would normally be done manually.
The term "application deployment framework" refers to a package of UrbanCode processes and actions used to manage entities in UrbanCode and run UrbanCode processes in a predefined way for a given application type. This allows the package of functionality to be developed, tested, and released with more predictable outcomes.
The UCADF provides actions that to manage these UCADF packages and move them from one UrbanCode instance to another.
There are a number of parts to the UCADF architecture that make all of this possible.
Part | Description |
---|---|
UCADF Actions Runner | The UCADF-Core-Library provides the UCADF actions runner that runs a defined set of actions. The actions runner is included in the UCADF-Core-Library JAR file. |
UCADF Core Plugin | The UCADF Core Plugin provides the ability to use the UCADF Actions Runner to run actions from an UrbanCode process step. The plugin is available from the UCADF-Core-Plugin open source project. |
UCADF Client | The UCADF Client (included in the UCADF-Core-Library) provides the ability to use the UCADF Actions Runner from the command line. (More information below.) |
UCADF Store | The UCADF Store is a set of utilities and a defined directory structure used to manage UCADFs across multiple UrbanCode instances. The store is available from the UCADF-Store open source project. |
A UCADF action is a Groovy/Java class that implements the action functionality using a UCADF action design pattern. THe design pattern includes the ability to:
- Provide complex property values to an action.-
- Have one action call another action.
- Return a complex object.
There are currently 260+ actions available as part of the UCADF Core Library that has extensive JavaDoc.
The UCADF-Package-Test/Test directory contains UCADF action files used to test the UCADF-Core-Library and provide usage examples.
Many UCADF actions can be run in a given order with the ability to conditionally control the flow of the action processing. This action processing is perform by the UCADF-Core-Plugin running an UrbanCode process step, or by running the ucadfclient command line utility. In both cases they process YAML (or JSON) information that describes the actions to be performed.
This is a simple example that initializes a specified property value, then runs a comment action that displays the value of the specified property.
# Initialize property values.
propertyValues:
MyProperty: "This is My Property"
# Run these actions.
actions:
- action: UcAdfComment
actionInfo: false
comment: "MyProperty=%s"
values:
- "MyPropertyValue"
- action: UcAdfComment
actionInfo: false
comment: "The end."
This would output:
MyProperty=MyPropertyValue
The end.
The actions runner processor begins with the following:
- Sets the actions runner properties from the system properties.
- Sets the actions runner properties from the actions property values.
- Sets the actions runner properties from the actions property files.
- Initialize a UCD session with initial connection information properties provided to the runner.
- Runs the specified actions using the properties provided for the action.
The actions runner maintains a cumulative set of properties during the life of the run. As each action is run the return value is placed in the runner's property collection where it can later be referenced. Properties are accessed using one of the following:
- ${u:propertyname} - Get the property and if not found then terminate with an error message.
- ${u?:propertyname} - Get the property and if not found then return an empty string.
Complex properties are accessed using a slash notation for each level of the property. Examples:
- ${u:foo} - Get the "foo" property.
- ${u:foo/bar} - Get the "foo" map value of "bar". Similar to Groovy "foo[bar]".
- ${u:foo/bar/1} - Get the "foo" map value of "bar" that is an array and the 1st element of that array. Similar to Groovy "foo[bar][1]".
- A property name that contains slashes may be enclosed in single or double quotes, e.g. “actionReturn’/originalRequest/myProperty’”
A property may have a Groovy-type Eval method that is evaluated each time the property is used. Examples:
- 'Eval("Dog".equals("${u:foo/bar/1}")' - Evaluates to true or false.
- 'Eval("Dog" + "${u:foo/bar/1}")' - Concatenates two strings.
Each action is processed as follows:
- Set the actions runner properties from the actions property values.
- Add the actions runner properties from the actions property files.
- If UCD session properties are specified for the action then initialize a UCD session using those properties, otherwise use the actions runner UCD session.
- Loads all of the action classes by looking through the classpath for Java packages named: org.urbancode.ucadf.*.action Thus, it is required for custom UCADFs to use that package naming standard.
- Runs the action.
- Return the action return value.
Each action may have a "when" property that identifies if the action should be run. This property must evaluate to a value of true or false. Examples:
actions:
# Evaluation in single quotes to be valid YAML.
- action: UcAdfComment
actionInfo: false
when: '"Dog".equals("${u:foo/bar/1}"'
comment: "My comment: %s."
values:
- "This is my comment"
# Evaluation block scalar to be valid YAML.
- action: UcAdfComment
actionInfo: false
when: |
"Dog".equals("${u:foo/bar/1}"
comment: "My comment: %s."
values:
- "This is my comment"
When an action completes, the object returned by the action is stored as the "actionReturn" property which is then available in subsequent actions as "${u:actionReturn}"
If the action has a value defined for the "actionReturnPropertyName" then the object returned by the action is also stored as that property name, e.g. actionReturnPropertyName: "MyProperty" is available to subsequent actions as "${u:MyProperty}"
The UCD session properties specify how the action(s) will connect to the UCD instance.
Property | Description |
---|---|
ucdUrl | If no value provided for the action then use the value provided as runner properties. If no runner property exists then use the value of the AH_WEB_URL system property. |
ucdUserId | If no value provided for the action then use the value provided as runner properties. |
ucdUserPw | If no value provided for the action then use the value provided as runner properties. |
ucdAuthToken | If no value provided for the action then use the value provided as runner properties. If no runner property exists then use the value of the AH_WEB_URL system property. |
- If ucdUserId and (ucdUserPw or ucdAuthToken) are provided then those are used for the connection.
- If no ucdUserId and (ucdUserPw or ucdAuthToken) then the ucdUserId="PasswordIsAuthToken" is used for the connection.
Most actions output information about how they are processing. Setting an action's actionInfo property to false will suppress most of this information.
The UCADF Core has general actions available that control the flow of action processing and perform other utility-type actions.
Outputs a comment using a printf-type evaluation of values.
An optional "when" value be specified to determine if the action should be performed.
actions:
- action: UcAdfComment
actionInfo: false
when: 'true'
comment: "My comment: %s."
values:
- "This is my comment"
Would output:
My comment: This is my comment.
The UCADF supports a variety of loop actions. These loop actions can all use the following features.
-
The UcAdfContinue action can be used as a child action of a loop to determine when certain items should be skipped.
-
The UcAdfBreak action can be used as a child action of a loop to determine when the loop should be terminated.
-
An optional when value be specified to determine if the loop should be performed.
This loop type iterates over the items (may be list or map) and performs the specified actions. By default the "item" property value is set for every iteration but that property name may be overridden by specifying an "itemProperty" value.
actions:
- action: UcAdfItemsLoop
actionInfo: false
when: 'true'
waitIntervalSecs: 2
items:
- "MyItem1"
- "MySkipItem"
- "MyItem2"
- "MyBadItem"
- "MyLastItem"
itemKeyProperty: "myItemKey"
itemProperty: "myItem"
actions:
- action: UcAdfLoopContinue
actionInfo: false
when: '"MySkipItem".equals("${u:myItem}")'
- action: UcAdfLoopBreak
actionInfo: false
when: '"MyBadItem".equals("${u:myItem}")'
- action: UcAdfComment
actionInfo: false
comment: "My item key is ${u:myItemKey} and item is ${u:myItem}."
The above would output the comment for items with a 2 second wait between them:
Skipping action [UcAdfLoopContinue}] when [false].
Skipping action [UcAdfLoopBreak}] when [false].
My item key is 0 and item is MyItem1.
Skipping action [UcAdfLoopContinue}] when [false].
Skipping action [UcAdfLoopBreak}] when [false].
My item key is 2 and item is MyItem2.
Skipping action [UcAdfLoopContinue}] when [false].
This loop type can be used for a wait (polling) loop. You may specify the following combination of properties:
- waitIntervalSecs and maxWaitSecs
- waitintervalSecs, maxWaitSecs, and maxTries
- waitIntervalsecs and maxTries
- maxTries
Example:
actions:
- action: UcAdfWaitLoop
waitIntervalSecs: 2
maxWaitSecs: 600
maxTries: 10
actions:
- action: UcAdfLoopBreak
when: 'Some condition here'
The above would retry every 2 seconds for a maximum of 600 seconds or 10 tries, whichever comes first. The UcAdfLoopBreak is used to exit the loop when the desired condition exists.
The UcAdfCounterLoop action may also be used for a counter loop.
Example:
actions:
- action: UcAdfCounterLoop
counterBegin: 1
counterChange: 1
counterEnd: 2
actions:
- action: UcAdfComment
actionInfo: false
comment: "Counter control %s"
values:
- ${u:counterLoopControl}
The above would output.
Counter control {counterBegin=1, counterChange=1, counterEnd=2, counterValue=1}
Counter control {counterBegin=1, counterChange=1, counterEnd=2, counterValue=2}
The UcAdfPageLoop action may also be used for a page loop needed for certain actions that can return a large number of items. The loop defines a property name that stores that page control information throughout the invocation of the child actions.
Example:
actions:
- action: UcAdfPageLoop
actionInfo: false
rowsPerPage: 20
actions:
- action: UcdGetVersions
actionInfo: false
component: "${u:coreTestComp1}"
actionReturnPropertyName: versions
- action: UcAdfItemsLoop
actionInfo: false
items: ${u:versions}
actions:
- action: UcAdfComment
actionInfo: false
comment: "Component [%s] Version [%s] Page [%s]"
values:
- ${u:item/component/name}
- ${u:item/name}
- ${u:pageLoopControl/pageNumber}
The above would output.
Component [UCADFCORETEST-Comp1] Version [201909021402560343] Page [{rowsPerPage=20, pageNumber=1, rangeStart=0, rangeEnd=20, size=98, pages=5}]
Component [UCADFCORETEST-Comp1] Version [201909021402560929] Page [{rowsPerPage=20, pageNumber=1, rangeStart=0, rangeEnd=20, size=98, pages=5}]
...
This action will run actions contained in a file.
Property | Description |
---|---|
fileName | The file name. |
fileType | The file type. Values: AUTO, JSON, YAML. Default is AUTO. |
propertyValues | Property values provided to the run files action, e.g. command line options from UcAdfClient. |
propertyFiles | Property files provided to the run files action, e.g. command line options from UcAdfClient. |
Processing:
- Parses the file into UcAdfActions.
- If first run action then initialize the actions runner.
- If propertyValues were specified then set those in the actions runner.
- If propertyFiles were specified then add those to the actions.
- Runs the actions with the UcAdfActionsRunner.
Set action properties to be used by the actions runner.
Property | Description |
---|---|
propertyValues | Property values provided to the run files action and that are evaluated when the action is parsed, e.g. command line options from UcAdfClient. |
actions:
- action: UcAdfSetActionProperties
propertyValues:
myProperty: 'foo'
bar: "dog"
epoch: 'Eval(new Date().getTime())'
Usage note: This action may be used to set multiple properties. If one property’s value is evaluated from another property’s value in the same action then the first property’s value is not evaluated as static in the subsequent usage. In the above example, if there were a second property that used the epoch value then that second property value could end up with a different epoch value than the one set as the static value. Use two different actions to handle this scenario.
actions:
- action: UcAdfSetActionProperties
setFinal: true
propertyValues:
epoch: 'Eval(new Date().getTime())'
If you want a value to be set once and never overriden then use the setFinal option.
If the code needed to set a property value is too complex for Eval to handle then the property can be set by running Groovy script. The action's information is made available to the script as the 'action' object. That can be used to access the 'actionsRunner' which can then be used to get/set action properties.
actions:
- action: UcAdfRunGroovyScript
actionReturnPropertyName: "foo"
scriptText: |
// Get an actions runner property value.
Map concatMap = action.getActionsRunner().getPropertyValue('myMap')
// Concatenate the string.
concatStr = concatMap.collect { k, v -> "$k=$v" }.join(", ")
// Set an actions runner property value.
action.getActionsRunner().setPropertyValue('dog') = concatStr
// Return a value as the action return property.
return concatStr
Actions may be nested.
actions:
- action: UcAdfItemsLoop
actionInfo: false
items:
- "MyItem1"
- "MyItem2"
itemProperty: "myItem"
actions:
- action: UcAdfWhen
actionInfo: false
when: '"MyItem1".equals("${u:myItem}")'
actions:
- action: UcAdfItemsLoop
actionInfo: false
items:
- "MyChildItem1"
- "MyChildItem2"
itemProperty: "myChildItem"
actions:
- action: UcAdfComment
actionInfo: false
comment: "My item is ${u:myItem} child ${u:myChildItem}."
elseActions:
- action: UcAdfComment
comment: "This is the else condition."
This would output:
My item is MyItem1 child MyChildItem1.
My item is MyItem1 child MyChildItem2.
Skipping action [UcAdfWhen}] when [false].
It is possible to do recursion by using the UcdRunActionsFromFile action with a when condition to have an action file run itself until the condition is met.
The UrbanCode security API terminology can be a bit confusing. The UCADF actions use some different terminology to (hopefully) alleviate some of that confsion.
UCADF Term | UCD API Term | Description |
---|---|---|
SecurityType | resourceType | Examples: Application, Component, Environment. |
SecuritySubtype | resourceRole | What you see if you click on the “Type Configuration” tab. These names must be unique across all security types, e.g. Agent and Application can’t have the same subtype name. Example: The default subtype of Application is “Standard Application”. |
SecurityPermission | resourceRole action | The specific security check boxes you see for roles. |
The ucadfclient functionality is included in the UCADF-Core-Library JAR.
For Windows there is a ucadfclient.cmd file that is a wrapper to run the ucadfclient.
ucadfclient command line options:
Option | Description |
---|---|
-f | Actions file to run. |
-p | (Optional) Properties files to use. Multiple allowed. |
-D | (Optional) Properties to define. Multiple allowed. |
-d | (Optional) Output verbose debugging information. |
The UCADF-Core-Library is published to Maven where it can be consumed as a build-time dependency when developing custom UCADF actions.
The UCADF-Core-Package is a zip file published to Maven that contains athe deployable set of files needed for runtime operation.
The Maven build requires the following properties to be defined.
Property | Description |
---|---|
MVN_REPO_ID | The Maven repository ID. |
MVN_REPO_NAME | The Maven repository name. |
MVN_REPO_URL | The Maven repository URL. |
Use the following commands to run the build and deploy the JAR to Maven:
export MVN_REPO_ID=MyRepoId
export MVN_REPO_NAME=MyRepoName
export MVN_REPO_URL=MyRepoURL
# Define the version number. If building with Jenkins the last digit could be the Jenkins ${BUILD_NUMBER}.
export UCADF_CORE_VERSION=1.0.0
# Set the version number in the pom.xml file.
mvn versions:set -DnewVersion="$UCADF_CORE_VERSION"
# Build, package, and deploy the library.
mvn -U clean deploy
After that build completes then use the following commands to build the UCADF-Core-Package zip file and deploy it to Maven:
# Set the version number in the package-pom.xml file.
mvn -f package-pom.xml versions:set -DnewVersion="$UCADF_CORE_VERSION"
# Build and deploy the UCADF-Core-Package.zip file.
mvn -f package-pom.xml package deploy:deploy-file -Dfile="target/UCADF-Core-Package-$UCADF_CORE_VERSION.zip" -DgeneratePom=false -DgroupId=org.urbancode.ucadf.core -DartifactId=UCADF-Core-Package -Dversion="$UCADF_CORE_VERSION" -DrepositoryId="$MVN_REPO_ID" -Durl="$MVN_REPO_URL"
The UCADF-Package-Test/Test directory contains the UCADF action files and test data used to test the UCADF-Core-Library.
This is an example of running the actions file that runs all of the action tests.
From Eclipse
Program arguments:
-f UCADF-Package-Test/Test/allActionTests.yml -DUCADF_STORE=C:/Dev/git/UCADF-Store -DucAdfInstance=ucadfdev
VM arguments:
-Dlog4j.configurationFile=log4j2.xml
From the Command Line
ucadfclient -f UCADF-Package-Test/Test/Actions/allActionTests.yml -DUCADF_STORE=C:/Dev/git/UCADF-Store -DucAdfInstance=ucadfdev
UCD Version | With Patches | Status |
---|---|---|
7.0.1.2.1008304 | ucd-7.0.1.2-PH09559-DeletedTypesShowingAsAssignable ucd-7.0.1.2_PH11030_deletedResourceTypesSeemlessCreation |
All tests run and passed. |
7.0.4.2.1038002 | None | All tests run and passed. |
7.0.5.2.1050384 | None | All tests run and passed. |
The open source Notepad++ application is one option for edition YAML file. One nice feature is that you can comment out a block of YAML lines with Ctrl+K and uncomment a block with Ctrl+Shift+K.
The RESTful APIs used by the UCADF-Core library are defined by the UrbanCode Deploy WADLs available here:
https://instance/cli/application.wadl
https://instance/cli-internal/application.wadl
https://instance/rest/application.wadl
https://instance/security/application.wadl
https://instance/property/application.wadl
This project is licensed under the MIT License - see the LICENSE file for details