Using custom definitions

The platform supports adding your own OAM extensions to create new Components, Traits, Policies, and Workflow Steps that better adapt to your needs. This capability is able to substitute the approach of using custom Kubernetes operators in a multitude of use cases. By creating your own definition you will be able to configure which low-level entities are rendered in Kubernetes so this feature is oriented to experienced users that are already familiar with the Kubernetes tooling. The following sections describe how to create a custom definition and use it in an application.

Using CUE templates

The preferred and simplest method to include a new custom entity is by using a CUE template. The CUE language offers a programmatic approach to static templates so it is possible to operate on the properties provided by the user when launching an Application to determine how those are applied internally. The objective of the CUE template is to render one or more entities after its evaluation. For example, to create a new TraitDefinition, you will need to define the new entity and include the CUE template.

apiVersion: core.oam.dev/v1beta1
kind: TraitDefinition
metadata:
  annotations:
    definition.oam.dev/description: This is the trait description 
  name: example-trait
spec:
  schematic:
    cue:
      template: |
        // Define the outputs of this trait
        outputs: output1: {
        	apiVersion: "<api_version>"
        	kind:       "<kind>"
        	metadata: name: context.name
        	spec: {
        		...
        	}
        }

        parameter: {
        	// +usage=Port to launch the service.
        	port: int
        	// +usage=Path to be loaded
        	path: string
        }

In this entity you will define the rendered objects making use of the CUE language. The following section will describe how to create a custom component definition.


Creating your own ComponentDefinition

Creating a custom definition can be useful in situations where we would like to standarize operations, create custom extensions, or any other aspect that can benefit from such customization. In this example, we would consider a use case where an organization would like to create a custom ComponentDefinition for an elements that should be reused among all applications. This can be easily done by means of adding a component such as the following one:

apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
  annotations:
    definition.oam.dev/description: Deploy the default nginx server.
  name: org-webserver
spec:
  schematic:
    cue:
      template: |
        output: {
        	apiVersion: "apps/v1"
        	kind:       "Deployment"
        	spec: {
        		selector: matchLabels: "app.oam.dev/component": context.name

        		template: {
        			metadata: labels: {
        				"app.oam.dev/name":      context.appName
        				"app.oam.dev/component": context.name
        			}

        			spec: {
        				containers: [{
        					name:  context.name
        					image: "nginx:1.23.0"

        					if parameter["cpu"] != _|_ {
        						resources: {
        							limits: cpu:   parameter.cpu
        							requests: cpu: parameter.cpu
        						}
        					}

        					if parameter["memory"] != _|_ {
        						resources: {
        							limits: memory:   parameter.memory
        							requests: memory: parameter.memory
        						}
        					}
        				}
        				]
        			}
        		}
        	}
        }
        parameter: {
        	// +usage=Number of CPU units for the service, like `0.5` (0.5 CPU core), `1` (1 CPU core)
        	cpu?: string

        	// +usage=Specifies the attributes of the memory resource required for the container.
        	memory?: string
        }
        
  workload:
    definition:
      apiVersion: apps/v1
      kind: Deployment
    type: deployments.apps

In this case the component will automatically launch a Kubernetes Deployment with the nginx image already set. The component offers a set of parameters such as the cpu, or memory so that the application can customize those. While this is a minimal example, it also illustrates an approach to reduce the number of potential parameters on the existing components.


Launching an application that uses custom ComponentDefinitions

To launch an application using the previous org-webserver component type, let’s use the following application:

apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
  name:  example-app-custom-def
spec:
  components:
    - name: my-server
      type: org-webserver

Next, click on the Deploy app button and select “YAML Deploy”. A screen will be shown where you can directly paste both definition separated by --- as shown in the figure.

Deploy from YAML

and click “Deploy”. The application will be deployed using your own custom definition. Selecting the deployed app, will confirm the type of component being used.

Application deployed with a custom component definition


Removing a custom definition

Before removing a custom definition make sure that no active applications are using it as this will create problems with the reconcile process of the affected applications. To remove the definition, navigate to component definition windows as shown below and click on the remove button.

Removing a custom definition

Alternatively, the removal process can be done with the web UI after selecting the definition to be removed, or with the CLI with:

playground def <type> remove <name>

FAQ

Cannot create application

The platform may refuse creating an application with the following error:

playground apps create my-custom-component.yaml
[Internal] error creating entity caused by admission webhook "validating.core.oam.dev.v1beta1.applications" denied the request: field "schematic": Invalid value error encountered, cannot create the validation process context of app=<application_name> in namespace=<namespace>: evaluate base template app=<application_name> in namespace=<namespace>: invalid cue template of workload my-server: expected 'EOF', found '}'.

Which could indicate a mismatch between open and closed brackets.

Cannot create object when creating the application

If the application is created, but contains an error when applying the component as:

Dispatch: Found 1 errors. [(cannot create object: Deployment in version "v1" cannot be handled as a ...: []v1.Container: decode slice: expect [ or n, but found {, error found in #10 byte of ...|tainers":{"container|..., bigger context ...|:"<application_name>"}},...]

The error could be caused by an incorrect schematic inside the CUE template. For example, labels are not being included inside the proper metadata section, or declaring a section that does not expect that type such as using a single element when a list is expected.

What’s next