Provisioners are the engine of this framework. They are the components that do the work of configuring the cloud containers, deploying, undeploying, and managing a service's life-cycle orchestration. There are two other components that have a one-to-one relationship with a provisioner, the Application and the ProvisionerFactory. The Application is an externally facing (service) interface class the user must implement and register with Intersmash via the @Service annotation. The ProvisionerFactory is an internal service specific concrete class that evaluates the Application instance and returns the corresponding Provisioner class for the service or throws an exception if none is found.
Intersmash uses a class naming convention that shows a clear relationship between these three class types.
- ${ProductComponentName}Application
- ${ProductComponentName}Provisioner
- ${ProductComponentName}ProvisionerFactory
There are several categories of service provisioners in Intersmash. Each category has some unique provision requirements. (The full list of provided services can be found here.)
-
Application server images: Application server images require the identification of the image file to deploy and any server specific configuration data.
-
Database images: Database provisioning requires the image and items like user credentials, database name and mount path.
-
Operator services: Operator services automates the behavior needed to deploy a given service on cloud environments via APIs that leverage the Operator Framework.
-
Templated services: Some product components have named templates that are executed to generate a specific service version or configuration. The user must declare the template name in their Application implementation.
-
Helm Chart: Helm Chart based provisioning relies on Helm Chart value files.
-
Automatic: Automatic (or application dictated) provisioning is a way to delegate the whole provisioning process to the Application class, rather than depending on a provisioner.
Here are the steps in adding a new service to Intersmash.
- Create a new Application interface class which extends from an existing Application interface.
- Create a new Provisioner class which implements or extends an existing Provisioner interface or class respectively.
- Create a new ProvisionerFactory class which implements the ProvisionerFactory interface.
- Add your new ProvisionerFactory class to the service file, ./provisioners/src/main/resources/META-INF/services/org.jboss.intersmash.provision.ProvisionerFactory
- Provide a demonstration application in the examples directory.
Here is a simple example that adds a "spy" service to the framework.
package org.secret.agents;
import java.util.Collections;
import java.util.List;
import org.jboss.intersmash.application.openshift.HasEnvVars;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.api.model.EnvVar;
public interface SpyApplication extends OpenShiftApplication, HasSecrets,
HasEnvVars {
// OpenShift configuration
@Override
default List<Secret> getSecrets() {
return Collections.emptyList();
}
@Override
default List<EnvVar> getEnvVars() {
return Collections.emptyList();
}
}
package org.secret.agents;
// a long list of imports here
public class SpyProvisioner implements OpenShiftProvisioner<SpyApplication> {
private final SpyApplication spyApp;
private FailFastCheck ffCheck = () -> false;
public SpyProvisioner(@NonNull SpyApplication spyApp) {
this.spyApp = spyApp;
}
@Override
public SpyApplication getApplication() {
return spyApp;
}
@Override
public void deploy() {
deployImage();
}
@Override
public void undeploy() {
// implement the logic for provisioning a "Spy" service instance on OpenShift
}
@Override
public void scale(int replicas, boolean wait) {
openShift.scale(spyApp.getName(), replicas);
if (wait) {
waitForReplicas(replicas);
}
}
public void waitForReplicas(int replicas) {
OpenShiftWaiters.get(openShift, ffCheck).areExactlyNPodsReady(replicas, spyApp.getName()).level(Level.DEBUG)
.waitFor();
WaitersUtil.serviceEndpointsAreReady(openShift, getApplication().getName(), replicas, 8080)
.level(Level.DEBUG)
.waitFor();
if (replicas > 0) {
WaitersUtil.routeIsUp(getUrl(spyApp.getName(), false))
.level(Level.DEBUG)
.waitFor();
}
}
@Override
public List<Pod> getPods() {
return openShift.getPods(getApplication().getName());
}
private void deployImage() {
// This is an long task of many steps and thus will not be
// listed here. Look at existing provisioners for examples.
}
}
package org.secret.agents;
import org.jboss.intersmash.application.Application;
import org.jboss.intersmash.provision.ProvisionerFactory;
public class SpyProvisionerFactory implements ProvisionerFactory<SpyProvisioner> {
@Override
public SpyProvisioner getProvisioner(Application application) {
if (SpyApplication.class.isAssignableFrom(application.getClass()))
return new SpyProvisioner((SpyApplication) application);
return null;
}
}
Edit service file, ./provisioners/src/main/resources/META-INF/services/org.jboss.intersmash.provision.ProvisionerFactory and add org.secret.agents.SpyProvisionerFactory and rebuild Intersmash.
The last task is to write and add a simple application that demonstrates the service working in the examples directory. Provide a README file that describes the service, how to configure and run it.
Application server images require the identification of the image file to deploy and any server specific configuration data. A provisioner should implement the OpenShiftProvisioner interface. WildflyImageOpenShiftProvisionerFactory provides a reference to a Provisioner and Application implementing this feature.
Database provisioning requires the image and items like user credentials, database name and mount path. Abstract class, DBImageOpenShiftProvisioner provides basic configuration. A provisioner's concrete class provides the extension to these types of services. MysqlImageOpenShiftProvisionerFactory provides a reference to a Provisioner and Application implementing this feature.
Operator services automates the behavior needed to deploy a given service on cloud environments via APIs that leverage the Operator Framework. Operator-based provisioners implement a common contract and high level behavior which is defined by Intersmash's, abstract class, OperatorProvisioner. Operator provisioning How-to, describes the steps needed to create a new operator-based provisioner. KeycloakOperatorProvisionerFactory provides a reference to a Provisioner and Application implementing this feature.
A list of operator based provisioners here
Some product components have named templates that are executed to generate a specific service version or configuration. The user must declare the template name in their Application implementation. The parent interface class for these provisioners is OpenShiftProvisioner. RhSsoTemplateOpenShiftProvisionerFactory provides a reference to a Provisioner and Application implementing this feature.
Helm Chart based provisioning relies on Helm Chart value files. It requires the implementation of interface, HelmChartRelease, which defines the contract for implementing classes to represent a Helm Chart release. The provisioner expects the application descriptor to be able to serialize the release data onto a file, which eventually is used to install or upgrade a given application service release. Abstract class, HelmChartOpenShiftProvisioner, holds the behavioral logic described. A concrete implementation of this class provides extension logic to fit various service types. WildflyHelmChartOpenShiftProvisionerFactory provides a reference to a Provisioner and Application implementing this feature.
Automatic (or application dictated) provisioning is a way to delegate the whole provisioning process to the Application descriptor, rather than depending on a provisioner.
Having a concrete instance of an Application class that implements interface
AutoProvisioningOpenShiftApplication will let the user add methods that map to the
provisioning process workflow, e.g.: deploy
, scale
, undeploy
.
OpenShiftAutoProvisionerFactory
provides a reference to a Provisioner and Application implementing this feature.
A list of provisioners by product can be found here.