Backend Component
Each plugin must have a backend component and is used for the core interactions with IcePanel. They can use one of two processor types.
- child_processor (Fork) - Used by Engine plugins
- vm2 (VM with VMScript) - Used by App, Network, Tool and Volume plugins
Backend plugins can only have static methods called while they are still a prototype. Once deployed into an environment then one class instance is created for each deployed item.
Dependencies should be bundled with your plugin to avoid version conflicts and to allow them to be self contained. See the recommended Webpack config for more information on how to do do this while excluding packages that are supplied by IcePanel as standard.
Plugin classes
To create a plugin type extend one of the following classes from the @icepanel/core package.
Use the appropriate base class and interface type then export it as the default class for your module.
import { AppPlugin, IAppPlugin } from '@icepanel/core'
// exporting the class will make it available for IcePanel to instantiate
export default class MyApp extends AppPlugin implements IAppPlugin {}
Each plugin type has an IcePanel defined state object which is used to access common properties.
See the below example for how to get and set the status from the plugin state.
import { AppPlugin, IAppPlugin, IAppStatus } from '@icepanel/core'
export default class MyApp extends AppPlugin implements IAppPlugin {
async setStatus(status: IAppStatus) {
// root objects are merged when set
await this.setState({ status })
console.log(`set app status to ${status}`)
}
getStatus() {
return this.state.status
}
}
Persistent data store
You can persist and retrieve Javascript objects using the plugin store. Just specify your store schema and pass it into the plugin class as a generic type. For example a terminal tool plugin may need to save a history of the last commands executed.
See the below example for how to set and get from the plugin store.
import { AppPlugin, IAppPlugin } from '@icepanel/core'
// interfaces can be used to specify the data types of for an object
export interface IMyAppStore {
vegetable?: string
fruit?: string
}
export default class MyApp extends AppPlugin<IMyAppStore>
implements IAppPlugin<IMyAppStore> {
async setVegetable(vegetable: string) {
// root objects are merged when set
await this.setStore({ vegetable })
console.log(`set vegetable ${vegetable}`)
}
getVegetable() {
return this.store.vegetable
}
}
The store is serialized into BJSON documents and so some limits apply.
Lifecycle hooks
Check out the IPlugin interface for lifecycle hooks common to all plugin types.
See the below example on how to check for changes to the store.
import { AppPlugin, IAppPlugin } from '@icepanel/core'
export interface IMyAppStore {
vegetable: string
fruit?: string
}
export default class MyApp extends AppPlugin<IMyAppStore>
implements IAppPlugin<IMyAppStore> {
async init() {
// we must call the superclass method
await super.init()
// set the default vegetable
await this.setStore({
vegetable: 'cabbage'
})
}
// triggered when a change has happened to a key inside the store
storeDidUpdate(prevStore: IMyAppStore) {
// check whether store vegetable has changed
if (this.store.vegetable !== prevStore.vegetable) {
console.log(`vegetable changed to ${this.store.vegetable}`)
}
}
async destroy() {
// we must call the superclass method
await this.destroy()
console.log(`last vegetable is ${this.store.vegetable}`)
}
}
Required static methods
The following static methods are required when writing App, Tool, Network and Volume plugins so that Engine plugins can deploy them.
- createPrototype - Generate an empty model to be added to the prototype under a name.
- createToolPrototype - Model which should be generated for a tool plugin, or null if unsupported.
- createEngineConfig - Map a prototype model into config which is used for engine deployment.
import {
AppPlugin,
IAppPlugin,
Export,
IModel,
PluginType,
createToolModel,
IAppModel,
IAppStatus,
IPrototypeModel,
IEnvironmentModel,
createAppModel,
createConnectorModel
} from '@icepanel/core'
export default class MyApp extends AppPlugin implements IAppPlugin {
// export is required to make this method available to external plugins
@Export()
static createPrototype(prototype: IPrototypeModel, name: string): IAppModel {
return {
...createAppModel(),
// these properties are passed through to createEngineConfig
engine: {
image: 'httpd:latest'
},
// these node connectors are shown in the UI
connectors: [
{
...createConnectorModel(),
name: 'network',
type: 'network'
}
]
}
}
@Export()
static createEngineConfig(environment: IEnvironmentModel, model: IAppModel) {
// this is passed through into the engine plugin so it can be mapped into the engine itself
return model.engine
}
@Export()
static createToolPrototype(target: IModel): IToolModel | null {
if (target.plugin.type === PluginType.App) {
// returning an object here will allow this plugin to be installed as a tool
return createToolModel()
} else {
// returning null here will hide this plugin from the tool install dropdown
return null
}
}
}