GuidesAPI Reference
Log In

Configuring a Runtime Extension

To define a runtime extension, create a Prodvana config file. You will need a docker image that does the actual apply in the runtime and a Kubernetes runtime already linked where your job will be running. See Configuring a Runtime for how to link a Kubernetes runtime.

runtime:
  name: my-custom-runtime
  extension:
    proxyRuntime:
      runtime: my-kubernetes-runtime  # pick a kubernetes runtime already linked with Prodvana
    apply:
      taskConfig:
        program:
          cmd:
          - apply-changes
          - --my-change-id={{.Params.changeId}}
          imageTag: my-tag
          imageRegistryInfo:
            containerRegistry: my-registry-name  # pick any registry already linked with Prodvana
            imageRepository: my-repository
    parameters:
    - name: changeId  # parameters can be whatever makes sense for your runtime
      description: the ID to deploy
      string:
        defaultValue: default-id

In the example above, the runtime extension is defined with the apply command apply-changes --my-change={{.Params.changeId}}, running in the my-kubernetes-runtime using the image my-repository:my-tag in the registry my-registry-name. Further more, changeId is defined as a parameter. This is useful as it will allow services deployed to the runtime to customize its behavior, e.g. picking a different VM image or specifying the replica count.

Apply the config with pvnctl configs apply.

pvnctl configs apply my-custom-runtime.pvn.yaml

To use the runtime, reference it in your application config in each of your release channel. It is also possible to have different runtime extensions be used for different release channels, e.g. to have production use a different set of commands than staging.

application:
  name: my-app # replace with your desired app name
  releaseChannels:
  - name: staging
    runtimes:
    - runtime: my-custom-runtime
      type: EXTENSION
  - name: production
    runtimes:
    - runtime: my-other-custom-runtime  # can be the same runtime as the previous step
      type: EXTENSION
    preconditions:
    - releaseChannelStable:
        releaseChannel: staging
    - manualApproval: {}

Next, define a service that uses the runtime extension.

service:
  name: my-service
  application: my-application
  runtimeExtension:
    parameterValues:
    - name: changeId
      string: my-change-id-for-this-service

It is also possible to have the service itself define parameters which are then passed through to the runtime, allowing this parameter to be set from the deploy UI.

service:
  name: my-service
  application: my-application
  runtimeExtension:
    parameterValues:
    - name: changeId
      string: "{{.Params.changeId}}"
  parameters:
  - name: changeId
    string:
      defaultValue: my-default-change-id

Common Examples

Using an Existing Namespace

By default, runtime extension commands will run in an automatically generated namespace, pvn-proxy-runtime-{runtime-name}. However, it can be useful to run commands in an existing namespace created outside of Prodvana, e.g. to reference an existing Kubernetes secret.

To run commands in an existing namespace, modify the runtime config:

runtime:
  name: my-custom-runtime
  extension:
    proxyRuntime:
      runtime: my-kubernetes-runtime  # pick a kubernetes runtime already linked with Prodvana
      containerOrchestration:
        k8s:
          namespace: my-existing-namespace
    ... # the rest of your config here

You are responsible for making sure the namespace exists.

Using Multiple Runtime Extensions

It is possible to have multiple types of runtime extensions configured in a release channel. For example, you may have one kind of runtime for deploying VMs and another for pushing static assets. To do so, define a name for the runtime:

application:
  name: my-app # replace with your desired app name
  releaseChannels:
  - name: staging
    runtimes:
    - runtime: my-vm-runtime
      type: EXTENSION
      name: vm
    - runtime: my-static-assets-runtime
      type: EXTENSION
      name: static
  - name: production
    runtimes:
    - runtime: my-prod-vm-runtime  # can be the same or different from staging
      type: EXTENSION
      name: vm  # this name must match the one defined in staging
    - runtime: my-prod-static-assets-runtime  # can be the same or different from staging
      type: EXTENSION
      name: static
    preconditions:
    - releaseChannelStable:
        releaseChannel: staging
    - manualApproval: {}

Then, in your service config, reference the name to decide which runtime to use.

service:
  name: my-service
  application: my-application
  runtimeExtension: ...  # rest of your config here
  runtimeConnection: vm

Using Kubernetes Configurations

To define commands via Kubernetes configurations, use kubernetesConfig instead of taskConfig in your runtime config file.

runtime:
  name: my-custom-runtime
  extension:
    proxyRuntime:
      runtime: my-kubernetes-runtime  # pick a kubernetes runtime already linked with Prodvana
    apply:
      kubernetesConfig:
        type: KUBERNETES
        local:
          path: path/to/kubernetes/job.yaml
    parameters:
    - name: changeId  # parameters can be whatever makes sense for your runtime
      description: the ID to deploy
      string:
        defaultValue: default-id

🚧

Define Exactly One job and Nothing Else

Defining any other Kuberntes resources here (e.g. configmap) will result in undefined behaviors due to conflicts when multiple services use this runtime extension.

Parameters can still be used, directly inside the Kubernetes configs themselves. The order of operation with parametrization is the same as with service configurations, see Configuring Services.

Defining a Fetch Command

Defining a fetch function allows Prodvana to continuously converge services in your runtime as well as skip the apply command if the service is already at the correct state. You should define a fetch function if you have the ability to query your runtime to figure out the state of your service.

A fetch command is just a run-to-completion job with special meaning behind its exit codes:

  • exit code 0 - The service is already at the desired state. Prodvana will not run the apply command.
  • exit code 2 - The service is not at desired state. Prodvana will run the apply command, pending any preconditions.
  • any other exit codes - Unhandled error, Prodvana will not take any actions.

If defined, Prodvana will run the fetch command periodically as part of its convergence loop. If the fetch command exits 0, Prodvana does nothing and marked the service as converged. This is the equivalent of pushing a Kubernetes service where the service is already at the desired version. If the fetch command exits 2, Prodvana will run the apply command after preconditions allow it to, potentially retrying until the fetch command returns 0. If the fetch command exits with any other exit code, Prodvana will take no action.

runtime:
  name: my-custom-runtime
  extension:
    fetch:
      kubernetesConfig:
        type: KUBERNETES
        local:
          path: path/to/kubernetes/job.yaml
    ... # rest of config here
runtime:
  name: my-custom-runtime
  extension:
    fetch:
      taskConfig:
        program:
          cmd:
          - fetch-state
          - --my-change-id={{.Params.changeId}}
          imageTag: my-tag
          imageRegistryInfo:
            containerRegistry: my-registry-name  # pick any registry already linked with Prodvana
            imageRepository: my-repository
    # rest of config here

Defining a getInfo Command

Defining a getInfo command is a way to help Prodvana display contextual information for owners of services running on Runtime Extensions, without needing to understand how Runtime Extensions work. You should define a getInfo command if displaying additional data specific to your Runtime Extension will improve the developer experience.

A getInfo command is a run-to-completion job that outputs structured data. If defined, Prodvana will run the getInfo command periodically as part of its convergence loop. The output of the command, whether it succeeds or fails, has no impact on the convergence behavior of the service.

To define a getInfo command, first create a Docker image with pvn-wrapper installed. pvn-wrapper is required in order for the process to return structured output to Prodvana, as Kubernetes does not separate stdout and stderr.

Then, create a program that writes json of the following schema to stdout:

{
  "outputs": [
    {
      "name": "Some User Readable Name, used when displaying",
      "text": "free-form text used to display"
    }
  ]
}

Then, create a Kubernetes Job configuration and reference it in your Runtime Extension config file.

apiVersion: batch/v1
kind: Job
metadata:
  generateName: get-info-
spec:
  ttlSecondsAfterFinished: 600
  template:
    spec:
      containers:
        - name: get-info
          image: your-docker-image
          command: ["pvn-wrapper", "exec", "--"]  # This is the important part. The rest of the file can be adapted as you'd like.
          args: ["your-binary-here", ...]
      restartPolicy: Never
  backoffLimit: 0

runtime:
  name: my-custom-runtime
  extension:
  	getInfo:
      kubernetesConfig:
        type: KUBERNETES
        local:
          path: path/to/kubernetes/job.yaml
  ... # rest of config here

For very simple commands, it is also possible to use Prodvana's backend-agnostic config:

runtime:
  name: my-custom-runtime
  extension:
  	getInfo:
      taskConfig:
        program:
          entrypoint:
          - pvn-wrapper
          - exec
          - --
          cmd:
          - your-binary-here
          imageTag: my-tag
          imageRegistryInfo:
            containerRegistry: my-registry-name  # pick any registry already linked with Prodvana
            imageRepository: my-repository
    # rest of config here

The output of the getInfo command will be displayed in the convergence sidebar on the Prodvana UI.

Like fetch and apply, getInfo has access to any parameters you defined for your Runtime Extension.

Requiring approval before each change

For critical runtime extensions or Terraform modules, it is useful to require manual approval before any changes are applied. Every time drift is detected, Prodvana will wait for manual approval before applying any changes.

application:
  name: my-app # replace with your desired app name
  releaseChannels:
  - name: staging
    runtimes:
    - runtime: my-custom-runtime
      type: EXTENSION
  - name: production
    runtimes:
    - runtime: my-other-custom-runtime  # can be the same runtime as the previous step
      type: EXTENSION
    preconditions:
    - releaseChannelStable:
        releaseChannel: staging
    - manualApproval: 
        # Require approval before any changes are made.
        # Every time drift is detected, require approval before applying changes.
        everyAction: true

🚧

everyAction: true currently only works for runtime extensions and Terraform runners. It does nothing for Kubernetes services.

Customizing Convergence Grace Period

The convergence grace period is the time between when the apply command started running to when the fetch command returns a clean exit with no drift. If this time exceeds the configured limit, Prodvana will run the apply command again.

The default convergence period is 10m. To set it to something else, modify your runtime extension config.

runtime:
  name: my-custom-runtime
  extension:
    fetch: ...
    apply: ...
    convergenceGracePeriod: 86400s  # 1 day