---
# System prepended metadata

title: template proposal

---

# template proposal

## Motivation

When we create a function, we often need to define "input", "output", "scale options", "trigger", "pod template", etc., which are often the same or very similar in the same cluster. In order to reduce these repetitive operations, we need to introduce a template mechanism that templates common configurations and facilitates the simplification of function creation steps.

## Goals

- Provides a template mechanism that serves as a reference source for the same configurations.

  The scope of this template mechanism is as follows:

  - Input configuration
  - Output configuration
  - Scale options configuration
  - Trigger configuration
  - Pod template configuration
  - Image repo secret configuration

## Proposal

To accomplish the above task, we need to add a new CRD to host the template:

```yaml
apiVersion: openfunction.io/v1beta1
kind: Template
metadata:
  name: default
spec:
  params:
  functionTemplate:
    functionIO:
      pubsub: 
      bindings: 
    scaleOptions: 
    trigger: 
    podSpec: 
    build:
```

The specification and meaning of the attributes are explained as follows.

### Params

`spec.params: []map[string]string`

Definition of variables that can be used in templates.

```yaml
spec:
  params:
    - name: func_name
      value: "myfunction"
    - name: func_msg
      value: "Hello world!"
    - name: cron_schedule_period
      value: "@every 5s"
```

### FunctionTemplate

`spec.functionTemplate: *FunctionTemplate`

Definition of function templates.

#### FunctionIO

`spec.functionTemplate.functionIO: *FunctionIO`

Definition of the "input" and "output" templates of the function.

`spec.functionTemplate.functionIO.pubsub & bindings: map[string]*componentsv1alpha1.ComponentSpec`

Definition of "bindings" and "pubsub" component templates for Dapr.

```yaml
spec:
  functionTemplate:
    functionIO:
      pubsub:
        kafka-server:
          type: pubsub.kafka
          version: v1
          metadata:
            - name: brokers
              value: "kafka-server-kafka-brokers:9092"
            - name: authRequired
              value: "false"
            - name: allowedTopics
              value: "pubsub"
        nats-streaming-server:
          type: pubsub.natsstreaming
          version: v1
          metadata:
            - name: natsURL
              value: "nats://localhost:4222"
            - name: natsStreamingClusterID
              value: "clusterId"
      bindings:
        cron:
          type: bindings.cron
          version: v1
          metadata:
            - name: schedule
              value: "@every 2s"
```

#### ScaleOptions

`spec.functionTemplate.scaleOptions: map[string]*ScaleOptions`

Definition of the function "Scale options" template.

```yaml
spec:
  functionTemplate:
    scaleOptions:
      myKeda:
        maxReplicas: 10
        minReplicas: 1
        keda:
          pollingInterval: 30
          cooldownPeriod: 60
          advanced:
            horizontalPodAutoscalerConfig:
              behavior:
                scaleDown:
                  stabilizationWindowSeconds: 60
                  policies:
                    - type: Percent
                      value: 50
                      periodSeconds: 15
                scaleUp:
                  stabilizationWindowSeconds: 0
```

#### Trigger

`spec.functionTemplate.trigger: map[string]*Triggers`

Definition of the function "Trigger" template.

```yaml
spec:
  functionTemplate:
    trigger:
      kafka-trigger:
        type: kafka
        metadata:
          topic: "logs"
          bootstrapServers: "kafka-server-kafka-brokers.default.svc.cluster.local:9092"
          consumerGroup: "logs-handler"
          lagThreshold: "20"
```

#### PodSpec

`spec.functionTemplate.podSpec: map[string]*v1.PodSpec`

Definition of the function "PodSpec" template.

```yaml
spec:
  functionTemplate:
    podSpec:
      has-init-container:
        initContainers:
          - name: init-myservice
            image: 'busybox:1.28'
            command:
              - sh
              - '-c'
              - >-
                until nslookup myservice.$(cat
                /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local;
                do echo waiting for myservice; sleep 2; done
      with-node-affinity:
        affinity:
          nodeAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              nodeSelectorTerms:
                - matchExpressions:
                    - key: kubernetes.io/hostname
                      operator: In
                      values:
                        - snail
```

#### Build

`spec.functionTemplate.Build: map[string]*BuildImpl`

Definition of the function "Build" template.

```yaml
spec:
  functionTemplate:
    build:
      build-go:
        builder: openfunction/builder-go:v2
        srcRepo:
            url: "https://github.com/xxx/abc.git"
            sourceSubPath: "golang/"
      build-nodejs:
        builder: openfunction/builder-node:v2
        srcRepo:
            url: "https://github.com/xxx/abc.git"
            sourceSubPath: "nodejs/"
```

### Rendering of template

The values of the parameters in `spec.functionTemplate` can be rendered by the values in `spec.params`.

In the following example, we set two variables in `spec.params`: "func_name_prefix" and "cron_schedule_period". 

Then use them in `spec.functionTemplate.functionIO` and `spec.functionTemplate.build` respectively.

The syntax for rendering is as follows:

"{{" + "params" + "." + Parameter Name + "}}"

The *rendering* works by finding the corresponding parameter name under `spec.params` and replacing `"{{params.<variable name>}}"` with its `value` .

```yaml
spec:
  params:
    - name: func_name_prefix
      value: of
    - name: cron_schedule_period
      value: '@every 5s'
  functionTemplate:
    functionIO:
      bindings:
        cron:
          type: bindings.cron
          version: v1
          metadata:
            - name: schedule
              value: '{{params.cron_schedule_period}}'
    build:
      env:
        FUNC_NAME_PREFIX: '{{params.func_name_prefix}}'
```

### Examples

Assume we have defined a function template:

```yaml
apiVersion: openfunction.io/v1beta1
kind: Template
metadata:
  name: default
spec:
  params:
    - name: func_name_prefix
      value: of
    - name: cron_schedule_period
      value: '@every 5s'
    - name: kafka_server
      value: 'kafka-server-kafka-brokers.default.svc.cluster.local:9092'
    - name: kafka_topics
      value: sample-topic
    - name: max_replicas
      value: '10'
    - name: min_replicas
      value: '1'
  functionTemplate:
    functionIO:
      pubsub:
        kafka-server:
          type: pubsub.kafka
          version: v1
          metadata:
            - name: brokers
              value: '{{params.kafka_server}}'
            - name: authRequired
              value: 'false'
            - name: allowedTopics
              value: '{{params.kafka_topic}}'
      bindings:
        cron:
          type: bindings.cron
          version: v1
          metadata:
            - name: schedule
              value: '{{params.cron_schedule_period}}'
    scaleOptions:
      myKeda:
        maxReplicas: '{{params.max_replicas}}'
        minReplicas: '{{params.min_replicas}}'
        keda:
          pollingInterval: 30
          cooldownPeriod: 60
          advanced:
            horizontalPodAutoscalerConfig:
              behavior:
                scaleDown:
                  stabilizationWindowSeconds: 60
                  policies:
                    - type: Percent
                      value: 50
                      periodSeconds: 15
                scaleUp:
                  stabilizationWindowSeconds: 0
    trigger:
      kafka-trigger:
        type: kafka
        metadata:
          topic: '{{params.kafka_topic}}'
          bootstrapServers: '{{params.kafka_server}}'
          lagThreshold: '20'
    podSpec:
      has-init-container:
        initContainers:
          - name: init-myservice
            image: 'busybox:1.28'
            command:
              - sh
              - '-c'
              - >-
                until nslookup myservice.$(cat
                /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local;
                do echo waiting for myservice; sleep 2; done
      with-node-affinity:
        affinity:
          nodeAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              nodeSelectorTerms:
                - matchExpressions:
                    - key: kubernetes.io/hostname
                      operator: In
                      values:
                        - snail
    build:
      build-go:
        builder: 'openfunction/builder-go:v2'
        env:
          FUNC_NAME_PREFIX: '{{params.func_name_prefix}}'
        srcRepo:
          url: 'https://github.com/xxx/abc.git'
          sourceSubPath: golang/
      build-nodejs:
        builder: 'openfunction/builder-node:v2'
        env:
          FUNC_NAME_PREFIX: '{{params.func_name_prefix}}'
        srcRepo:
          url: 'https://github.com/xxx/abc.git'
          sourceSubPath: nodejs/
```

Based on this function template, we can write Function as follows.

> `templateRef.name` from "template name" + "." + "\<the name of the corresponding part of the template\>"
>
>  i.e. `spec.build.templateRef.name` in Function ("default"(template name) + "." + "build-go"), means that it will look for the contents of `spec.functionTemplate.build.build-go` in the Template.

```yaml
apiVersion: core.openfunction.io/v1beta1
kind: Function
metadata:
  name: sample-a
spec:
  version: "v1.0.0"
  image: openfunctiondev/v1beta1-autoscaling-subscriber:latest
  imageCredentials:
    name: push-secret
  build:
    templateRef:
      name: default.build-go
  serving:
    runtime: "async"
    scaleOptions:
      templateRef:
        name: default.myKeda
    triggers:
      templateRef:
        - name: default.kafka-trigger
    template:
      templateRef:
        - name: default.has-init-container
    inputs:
      - name: producer
        component: kafka-server
        topic: "pubsub"
    pubsub:
      templateRef:
        - name: default.kafka-server
```

We will also support overriding parameters in Function:

> Priority of parameters: function level > template level
>
> This means that for the function in the following case, the minimum number of replicas will be set to 5, while other functions without this setting will still maintain the minimum number of copies of 1 as defined in the template.

```yaml
apiVersion: core.openfunction.io/v1beta1
kind: Function
metadata:
  name: sample-b
spec:
  input:
    params:
      - name: min_replicas
        value: 5
  version: "v1.0.0"
  image: openfunctiondev/v1beta1-autoscaling-subscriber:latest
  imageCredentials:
    name: push-secret
  build:
    templateRef:
      name: default.build-go
  serving:
    runtime: "async"
    scaleOptions:
      templateRef:
        name: default.myKeda
    triggers:
      templateRef:
        - name: default.kafka-trigger
    template:
      templateRef:
        - name: default.has-init-container
    inputs:
      - name: producer
        component: kafka-server
        topic: "pubsub"
    pubsub:
      templateRef:
        - name: default.kafka-server
```

Certainly we still support the way of running functions directly without using templates.