Yes.
If you wish to deploy an artifact type that is not in the list of supported MyST Artifact Types, you can do this through the MyST Extensibility Framework.
MyST provides an intuitive data model to define your OFMW components. In some scenarios, you might wish to extend MyST in order to achieve specific configuration and/or deployment needs.
MyST provides an extensible framework through declarative WLST or via custom actions to be executed at provision or deploy-time.
Custom actions can be written in Python, Jython, WLST, Ant, Java or even Puppet manifests.
MyST should be extended only when strictly necessary. You should use the out of the box MyST model as much as possible so that your solution is upgrade safe.
If you believe that you cannot achieve an outcome by using the MyST model, please contact Rubicon Red support for advice. The following order of preference should be taken when deciding how to model configuration and deployment within MyST.
myst-extension
).myst-extension
consider creating a custom action and type or raise it as a feature request with MyST Support.Custom deployment types can be supported in the MyST CLI through the following property name standard where
ID
is a unique identifier for the artifact instance, TYPE-ID
is a unique identifier for the artifact type andPARAM-ID
is a unique identifier for any artifact parameterscore.deployment[ID].artifact.repository.artifactId
core.deployment[ID].artifact.repository.groupId
core.deployment[ID].artifact.repository.version
core.deployment[ID].present
core.deployment[ID].type=TYPE-ID
core.deployment[ID].param[PARAM-ID]
For example, if we wanted to create an artifact type for an Oracle API Gateway Federation we may use the following:
core.deployment[OAG_CONFIG].artifact.repository.artifactId=OAG_CONFIG
core.deployment[OAG_CONFIG].artifact.repository.groupId=com.acme
core.deployment[OAG_CONFIG].artifact.repository.version=1.0.0-1
core.deployment[OAG_CONFIG].present=true
core.deployment[OAG_CONFIG].type=oag-fed
core.deployment[OAG_CONFIG].param[target-groups]=default_group
As with all MyST CLI resources, you can alternatively use XML instead of Name/Value Properties if you want:
<deployment id="OAG_CONFIG">
<artifact>
<repository>
<artifactId>OAG_CONFIG</artifactId>
<groupId>com.acme</groupId>
<version>1.0.0-1</version>
<type>jar</type>
</repository>
</artifact>
<present>true</present>
<type>oag-fed</type>
<param-list>
<param id="target-groups">default_group</param>
</param-list>
</deployment>
If you want to define your artifact instance properties within an existing Platform Blueprint or Model in MyST Studio you should use the name/value property notation define it under the Global Variables.
MyST supports the injection of Custom Actions within various parts of the Provisioning and Deployment workflow.
When using the CLI, custom actions should be defined and version-controlled under a specific folder within the MyST workspace depending on the language they are written in:
Language | Location | File Extension |
---|---|---|
Python / Jython | ext/targets/jython | .py |
WLST Script | ext/targets/wlst | .py |
Java Archive | ext/lib/thirdparty | .jar |
Puppet Manifest | ext/lib/puppet | .pp |
At runtime, MyST CLI will auto-discover these custom actions. To inject them before or after specific out-of-the-box MyST actions you can use the following property notation:
action.<ACTION NAME>.pre|post
For example, to execute an action called deploy-oag
after all of the other deployments are complete you could set the following:
action.deploy.post=deploy-oag
To prevent the custom actions from failing when there are no artifacts defined in the model matching the deploy type, you should develop the custom action to return without performing a deploy.
For example:
oagFeds=conf.getProperty('internal.deployment.oag-fed')
if oagFeds is None or len(oagFeds) == 0:
return
If you need to download or access artifacts from an Artifact Repository as part of a deployment, make sure that you run download-deploy-artifacts
prior to a deployment. This can be automatically done with the property action.deploy.pre=download-deploy-artifacts
. From your custom action you can then access the location of the downloaded artifact from ${MYST_WORKSPACE}/target/artifacts/${groupId}/${artifactId}-${version}.${type}
In terms of the scripting language and how it interacts with MyST, Python, Jython and WLST are all quite similar. The differences are as follows:
The action name will be the same name as the file, without the file extension. For example, if the file is called configure-mediator.py
, then the action name will be configure-mediator
.
At runtime, MyST passes all of the instance data via the conf
context. In a case where the values to be set in the action are either different per environment or could be changed often, it's recommended to externalise them into your model.
The runtime data can be accessed either as name/value properties or XMLBean
Java objects.
The unique IDs for a given deployment type can be accessed in a comma-separated list via internal.deployment.TYPE-ID
.
So in the previously mentioned OAG_CONFIG
example if we had two deployment IDs of OUTBOUND_OAG_CONFIG
and INBOUND_OAG_CONFIG
it would be internal.deployment.oag-fed=OUTBOUND_OAG_CONFIG,INBOUND_OAG_CONFIG
You could then access the deployment object properties like this:
oagFeds=conf.getProperty('internal.deployment.oag-fed')
if oagFeds is None or len(oagFeds) == 0:
return
deployFedsList = oagFeds.split(',')
for oagFedIdentifier in deployFedsList:
type = cfg['core.deployment[' + oagFedIdentifier + '].type']
name = cfg['core.deployment[' + oagFedIdentifier + '].artifactId']
targetGroups = cfg['core.deployment[' + oagFedIdentifier + '].param[target-groups]']
or if you want to use the XML Bean Object instead of properties, you can something like this:
deployFedsArray = model.getCore().getDeploymentList().getDeploymentArray()
for oagFedObject in deployFedsArray
type = oagFedObject.getType()
name = oagFedObject.getArtifact().getRepository().getArtifactId()
targetGroups = oagFedObject.getParamList().getParamArray()[0].getStringValue()
ext/targets/wlst/configure-mediator.py
from java.util import HashMap
def myst(conf):
if 'admin.host' in conf and 'admin.port' in conf and 'core.fmw.admin.username' in conf and 'core.fmw.admin.password' in conf:
LOG.info('Connecting to Admin Server')
admin_server_url=conf['admin.host'] + ':' + conf['admin.port']
connect(conf['core.fmw.admin.username'],conf['core.fmw.admin.password'],admin_server_url)
domainRuntime()
if 'servers' in conf:
servers=conf['servers']
serversserversList=servers.split(',')
for server in serversList:
serverName = conf["core.domain.server["+server+"].name"]
mediator= ObjectName('oracle.as.soainfra.config:Location='+serverName+',name=mediator,type=MediatorConfig,Application=soa-infra')
print 'DeferredWorkerThreadCount:'
print mbs.getAttribute(mediator, 'DeferredWorkerThreadCount')
mbs.setAttribute(mediator, Attribute('DeferredWorkerThreadCount',2))
disconnect()
ext/targets/jython/deploy-oag.py
execfile(System.getProperty("myst.home") + '/lib/targets/common/utils.py')
def myst(cfg):
oagFeds=conf.getProperty('internal.deployment.oag-fed')
if oagFeds is None or len(oagFeds) == 0:
return
deployFedsList = oagFeds.split(',')
scriptName = 'deploy-oag-fed-archive.py'
cfg['oag.remote.tmpdir'] = '/tmp/myst'
remoteTmpDir = cfg['oag.remote.tmpdir']
adminNode = cfg['product.wls-admin.node-list']
adminHost = cfg['core.node[' + adminNode + '].host']
cfg['remote.server.host'] = adminHost
cfg['remote.server.username'] = cfg['core.node[' + adminNode + '].authentication.credentials.username']
cfg['remote.server.password'] = cfg['core.node[' + adminNode + '].authentication.credentials.password']
cfg['destination.directory'] = cfg['oag.remote.tmpdir']
scriptLocation = System.getProperty('myst.workspace') + '/resources/oag'
cfg['library.file'] = scriptLocation + '/' + scriptName
if not 'noop' in cfg:
call_ant(cfg, '/lib/targets/ant/deploy-libs.xml','remote-copy-unix')
remoteCommand = 'mkdir -p ' + remoteTmpDir
cfg['remote.execute.command'] = remoteCommand
if not 'noop' in cfg:
call_ant(cfg, '/lib/targets/ant/deploy-libs.xml','remote-execute')
for deployFed in deployFedsList:
sourceLocation = # Get source location from binary
sourceFile = # TODO: Get source file from binary
targetGroups = cfg['core.deployment[' + deployFed + '].param[target-groups]']
cfg['destination.directory'] = remoteTmpDir
cfg['library.file'] = sourceLocation + '/' + sourceFile
print 'Copy file ' + cfg['destination.directory'] + '/' + cfg['library.file']
if not 'noop' in cfg:
call_ant(cfg, '/lib/targets/ant/deploy-libs.xml','remote-copy-unix')
targetGroupsList = targetGroups.split(',')
for targetGroup in targetGroupsList:
jythonFile = cfg['core.product[oag].home'] + '/apigateway/posix/bin/jython'
remoteCommand = 'cd ' + remoteTmpDir + '; ' + jythonFile + ' ' + scriptName + ' ' + adminHost + ' ' + cfg['core.product[oag].param[node-manager-port]'] + ' "' + cfg['core.product[oag].param[password]'] + '" ' + remoteTmpDir + '/' + sourceFile + ' ' + targetGroup
cfg['remote.execute.command'] = remoteCommand
if not 'noop' in cfg:
call_ant(cfg, '/lib/targets/ant/deploy-libs.xml','remote-execute')
remoteCommand = 'rm -f ' + remoteTmpDir + '/' + sourceFile
cfg['remote.execute.command'] = remoteCommand
if not 'noop' in cfg:
call_ant(cfg, '/lib/targets/ant/deploy-libs.xml','remote-execute')
remoteCommand = 'rm -f ' + remoteTmpDir + '/' + scriptName
cfg['remote.execute.command'] = remoteCommand
if not 'noop' in cfg:
call_ant(cfg, '/lib/targets/ant/deploy-libs.xml','remote-execute')
MyST also supports extension through a Java SDK. The JAR file containing the MyST extension and all the dependent JARs must be under <MYST_WORKSPACE>/lib/thirdparty
.
If some JARS must be loaded before the MyST JARs, then these files must be placed under <MYST_WORKSPACE>/lib/thirdparty/pre
.
Below is an example MyST Custom Action Java class:
package com.example;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.io.File;
import com.rubiconred.myst.actions.JavaActionRunner;
import com.rubiconred.myst.config.MySTConfiguration;
import com.rubiconred.myst.core.CoreDocument;
import com.rubiconred.myst.core.CustomType.Property;
import com.rubiconred.myst.exceptions.ActionException;
public class MyCustomMySTAction extends JavaActionRunner {
private String yourName;
private String yourFmwVersion;
@Override
public void run() throws ActionException {
MySTConfiguration configuration = getConfiguration();
String currentAction = configuration.getRequest().getAction();
System.out.println("Action: " + currentAction);
// Get value via Java Objects
CoreDocument coreDoc = configuration.getModel();
for (Property propObj : coreDoc.getCore().getCustom().getPropertyArray()){
if (propObj.getName().equals("your.name")){
yourName = propObj.getName();
}
}
yourFmwVersion = coreDoc.getCore().getFmw().getVersion();
System.out.println("Access via Java Object");
System.out.println("Hello "+yourName + ". I see you are using FMW "+ yourFmwVersion);
// Get value via Property Notation
Properties properties = configuration.getProperties();
System.out.println("Access via Java Object");
System.out.println("Hello "+ properties.getProperty("your.name") + ". I see you are using FMW "+ properties.getProperty("core.fmw.version") );
}
@Override
public List<String> getActionNames() {
List<String> actionNames = new ArrayList<String>();
actionNames.add("my-custom-myst-action");
return actionNames;
}
}
To make the class dynamically discoverable, it must be built into a jar which includes the file META-INF/services/com.rubiconred.myst.actions.JavaActionRunner
with the name of the class in the contents - e.g.
com.example.MyCustomMySTAction
If you have multiple custom actions in one JAR, each action class should be listed in the above files contents.
Any property with password
in the name will be automatically encrypted when it is defined in MyST.
To debug and test the MyST custom action outside of MyST, we will need myst-core.jar
, myst-model-*.jar
, xmlbeans-*.jar
and log4j-*.jar
libraries available on our classpath. These jars should exist under $MYST_HOME/lib
or $MYST_HOME/lib/thirdparty
Below is an example snippet for testing our MyST custom action, you could put this in a JUnit test or a Java main method.
// Load XMLBean
CoreDocument coreDoc = CoreDocument.Factory.parse(new File("/Users/rubiconred/test.xml"));
// Load Properties
Properties prop = new Properties();
prop.load(new FileInputStream(new File("/Users/rubiconred/test.properties")));
// Set MyST Home
System.setProperty("myst.home", "/opt/myst");
// Instantiate MyST Configuration
MySTConfiguration config = new MySTConfiguration();
config.setModel(coreDoc);
config.setProperties(prop);
// Instantiate MyST Custom Action
MyCustomMySTAction customAction = new MyCustomMySTAction();
customAction.setConfiguration(config);
// Run MyST Custom Action
customAction.run();
Notice that it assumes a myst.home
. Before running this test you should install the MyST CLI and set it's location for the value of myst.home
.
The test.properties
file and test.xml
file used in the above example can be generated from the MyST CLI as follows:
myst properties <model>
where <model>
is the name of the MyST Platform Model. For a list of available models, you should be able to type myst usage
.myst properties <model> -Dxml
where <model>
is the name of the MyST Platform Model. For a list of available models, you should be able to type myst usage
.