Skip to content

Package Manager Service

The Package Manager is a Go-based microservice responsible for the full lifecycle of Helm chart-based service deployments within ETSI HypO. It bridges SONATA's orchestration workflows with Kubernetes clusters by handling chart parsing, conversion, deployment, activation/deactivation, and termination — all driven through Kafka messaging.

ETSI HypO supports services packaged in three formats and normalises them all into Helm before deployment:

  • Helm charts sourced directly from Helm repositories, OCI registries, or Git repositories.
  • Docker-based services via docker-compose, converted using the Kompose library.
  • Kubernetes-native services packaged as plain Kubernetes manifests, also converted via Kompose.

The converted or downloaded chart is always pushed to an internal OCI registry before deployment, decoupling chart acquisition from chart installation.

API

At the northbound, the Package Manager listens to Kafka topics where SONATA publishes service lifecycle requests. Each topic maps to a dedicated handler. At the southbound, handlers interact with the Helm client, Kubernetes API, and the Registry API service.

Table 1: Package Manager Kafka API.
Northbound integration with Using Kafka topic Leads to southbound call Purpose
SONATA helm-engine-parse Helm/OCI/Git/Kompose libraries Fetch, convert, and store a chart in the internal OCI registry
SONATA helm-engine-deploy Registry API → Helm client Deploy a chart from the internal registry onto a K8s cluster
SONATA helm-engine-activation Registry API → Helm client Activate or deactivate a deployed service
SONATA helm-engine-termination Registry API → Helm client Uninstall a Helm release and clean up its namespace
Registry API get-package-registry-incoming-channel Internal handler routing Deliver credentials or kubeconfig needed by the above handlers
Table 2: Package Manager response topics.
Southbound integration with Using Kafka topic Purpose
SONATA helm-engine-parse-result Result of the chart parse and store operation
SONATA helm-engine-deploy-result Result of the Helm chart deployment
SONATA helm-engine-activation-result Result of the service activation or deactivation
SONATA helm-engine-termination-result Result of the Helm release termination
Registry API response-package-registry-outgoing-channel Request credentials or kubeconfig from Registry API

Note that helm-engine-deploy topic and corresponding Package Manager functionality also covers the case of a runtime service update. During this process, a service provider may PATCH a specific service in the TMF Service Inventory API, passing an updated version of the values.yaml of this service. This allows to easily upgrade/downgrade one or more service images, modify the replicas of specific components, introduce or change the resource limits, etc.

Helm Parse Flow

The parse flow is the first step of any end user service deployment. SONATA publishes a message to helm-engine-parse. The Package Manager fetches the chart, converts it to Helm if necessary, applies scheduler adapters, and pushes it to the internal OCI registry.

The incoming message is a Kogito-wrapped ServiceOrder:

{
  "specversion": "1.0",
  "id": "21000e26-31eb-43e7-8343-88a696fd96b1",
  "source": "/process/helmEngineParseRegistryProcess",
  "type": "helm-engine-parse",
  "kogitoprocinstanceid": "7aa45064-f4a7-4a25-a7a2-667afb07c3d1",
  "data": {
    "serviceOrderId": "2c4fecbc-c436-4f8c-a5db-4a1159ebad2a",
    "serviceId": "790daa4d-7f56-4575-8b64-c558cf518bed",
    "servicePackageManager": "helm",
    "serviceRepositoryURL": "https://charts.example.com/myrepo",
    "serviceArtifactIdentifier": "my-chart",
    "serviceArtifactVersion": "1.2.0",
    "servicePackageValuesYml": "<base64-encoded values.yaml>",
    "clusterManager": "",
    "clusterMetadata": ""
  }
}

The servicePackageManager field drives chart acquisition:

Value Source type Conversion applied
helm Helm repository None — used directly
oci OCI registry None — pulled via OCI client
docker Git repo with compose.yaml Docker Compose → Helm (Kompose)
kubernetes Git repo with K8s manifests Kubernetes manifests → Helm (Kompose)

If the source repository is private, the handler routes to the Registry API (via get-package-registry-incoming-channel) to retrieve credentials before fetching the chart. For OCM-based multi-cluster deployments, it also routes through Registry API so the scheduler namespace can be resolved and embedded into the chart annotations.

Upon success, the handler publishes a response to helm-engine-parse-result:

{
  "specversion": "1.0",
  "id": "21000e26-31eb-43e7-8343-88a696fd96b1",
  "source": "/process/helmEngineParseRegistryProcess",
  "type": "helm-engine-parse-result",
  "kogitoprocrefid": "7aa45064-f4a7-4a25-a7a2-667afb07c3d1",
  "data": {
    "serviceOrderId": "2c4fecbc-c436-4f8c-a5db-4a1159ebad2a",
    "serviceId": "790daa4d-7f56-4575-8b64-c558cf518bed",
    "helmEngineOrderStatus": "success",
    "helmEngineOrderMessage": "Helm charts were compiled and pushed successfully",
    "clusters": [
      {
        "locationId": "",
        "numberOfK8sNodes": 1
      }
    ]
  }
}

Helm Deploy Flow

After a successful parse, SONATA publishes a deployment request to helm-engine-deploy. The handler first requests the kubeconfig for the target Kubernetes cluster from the Registry API. Once the kubeconfig is delivered back via response-package-registry-outgoing-channel, the handler pulls the chart from the internal OCI registry, creates a Helm client, and installs the release.

The incoming message is a Kogito-wrapped HelmDeployRequest:

{
  "specversion": "1.0",
  "id": "21000e26-31eb-43e7-8343-88a696fd96b1",
  "source": "/process/helmEngineDeploymentProcess",
  "type": "helm-engine-deploy",
  "kogitoprocinstanceid": "7aa45064-f4a7-4a25-a7a2-667afb07c3d1",
  "data": {
    "serviceOrderId": "2c4fecbc-c436-4f8c-a5db-4a1159ebad2a",
    "serviceId": "790daa4d-7f56-4575-8b64-c558cf518bed",
    "activateService": true,
    "k8sServiceId": "c04f3d66-5350-49ea-8b31-ff325f149232",
    "servicePackageValuesYml": "<base64-encoded values.yaml override>",
    "requestedCompletionDate": "2048-02-13T11:27:56.705567622Z"
  }
}

The activateService flag controls the initial state of the deployment. When false, the release is installed and then immediately uninstalled with history kept (helm uninstall --keep-history), leaving the namespace intact but the service inactive and restorable.

The deployment namespace defaults to the serviceId. For OCM-based deployments, the namespace is overridden with the scheduler namespace extracted from the chart annotations during the parse phase.

Upon success, the handler publishes to helm-engine-deploy-result:

{
  "specversion": "1.0",
  "id": "21000e26-31eb-43e7-8343-88a696fd96b1",
  "source": "/process/helmEngineDeploymentProcess",
  "type": "helm-engine-deploy-result",
  "kogitoprocrefid": "7aa45064-f4a7-4a25-a7a2-667afb07c3d1",
  "data": {
    "serviceOrderId": "2c4fecbc-c436-4f8c-a5db-4a1159ebad2a",
    "serviceId": "790daa4d-7f56-4575-8b64-c558cf518bed",
    "helmEngineOrderStatus": "success",
    "helmEngineOrderMessage": "Helm charts were deployed successfully",
    "state": "ACTIVE",
    "releaseName": "790daa4d-release",
    "k8sIpAddress": "10.0.2.4",
    "serviceNamespaces": ["790daa4d-7f56-4575-8b64-c558cf518bed"],
    "serviceEndpoints": [
      { "ip": "10.0.2.4", "port": 8080 }
    ],
    "namespaceResourceQuota": "",
    "defaultValuesYaml": "<base64-encoded default values.yaml>"
  }
}

Helm Activation Flow

SONATA uses this flow to activate or deactivate a service. The message is published to helm-engine-activation. Similar to the deployment flow, the handler first requests the kubeconfig from the Registry API.

The incoming message is a Kogito-wrapped ActivationRequest:

{
  "specversion": "1.0",
  "id": "21000e26-31eb-43e7-8343-88a696fd96b1",
  "source": "/process/helmEngineActivationSubprocess",
  "type": "helm-engine-activation",
  "kogitoprocinstanceid": "7aa45064-f4a7-4a25-a7a2-667afb07c3d1",
  "data": {
    "serviceOrderId": "2c4fecbc-c436-4f8c-a5db-4a1159ebad2a",
    "serviceId": "790daa4d-7f56-4575-8b64-c558cf518bed",
    "activateService": true,
    "k8sServiceId": "c04f3d66-5350-49ea-8b31-ff325f149232"
  }
}

When activateService is true, the service is restored by rolling back to the latest Helm revision (helm rollback). Otherwise (activateService is false), the Helm release is uninstalled with history preserved (helm uninstall --keep-history), keeping the namespace and release history intact so the service can be reactivated later.

The response is published to helm-engine-activation-result and shares the same structure as the deploy response.

Helm Termination Flow

SONATA publishes a termination request to helm-engine-termination to permanently remove a service. The handler requests the kubeconfig from the Registry API, then uninstalls the Helm release.

The incoming message is a Kogito-wrapped TerminationRequest:

{
  "specversion": "1.0",
  "id": "21000e26-31eb-43e7-8343-88a696fd96b1",
  "source": "/process/helmEngineTerminationProcess",
  "type": "helm-engine-termination",
  "kogitoprocinstanceid": "7aa45064-f4a7-4a25-a7a2-667afb07c3d1",
  "data": {
    "serviceOrderId": "2c4fecbc-c436-4f8c-a5db-4a1159ebad2a",
    "serviceId": "790daa4d-7f56-4575-8b64-c558cf518bed",
    "k8sServiceId": "c04f3d66-5350-49ea-8b31-ff325f149232"
  }
}

For standard deployments, the namespace is deleted along with the release. For OCM-based deployments (where the scheduler namespace is set in chart annotations), the namespace is preserved because it is managed by the OCM scheduler, not by the Package Manager.

The response is published to helm-engine-termination-result:

{
  "specversion": "1.0",
  "id": "21000e26-31eb-43e7-8343-88a696fd96b1",
  "source": "/process/helmEngineTerminationProcess",
  "type": "helm-engine-termination-result",
  "kogitoprocrefid": "7aa45064-f4a7-4a25-a7a2-667afb07c3d1",
  "data": {
    "serviceOrderId": "2c4fecbc-c436-4f8c-a5db-4a1159ebad2a",
    "serviceId": "790daa4d-7f56-4575-8b64-c558cf518bed",
    "helmEngineOrderStatus": "success",
    "helmEngineOrderMessage": "Helm release termination was successful"
  }
}

Interaction with Registry API

The Package Manager never holds Kubernetes credentials itself. For every operation that requires a kubeconfig (deploy, activate, terminate), it sends a request to the Registry API via the Kafka channel get-package-registry-incoming-channel. The Registry API retrieves the stored secret from Vault and replies on response-package-registry-outgoing-channel.

For chart parse operations involving private repositories, the same mechanism is used to retrieve repository credentials (username/password for Helm repos, OCI registries, or Git).

The type field inside the response message determines which internal handler the Package Manager routes the message to:

type value Routed to handler
TypeParsing HandleParsing
TypeDeployment HandleDeployment
TypeActivation HandleActivation
TypeTermination HandleTermination

The two credential message structures are shown below.

Repository / registry credentials (used for chart fetch in the parse flow):

type RegistryURLMessage struct {
    Type               string                                          `json:"type"`
    KogitoServiceOrder kogito.RequestData[datamodel.ServiceOrder]      `json:"kogitoServiceOrder"`
    RepoCredentials    RepoCredentials                                 `json:"repoCredentials"`
}

type RepoCredentials struct {
    Username string `json:"username"`
    Password string `json:"password"`
}

Image pull secret + kubeconfig (used for deploy, activate, and terminate flows):

type PullSecretMessage struct {
    Type              string                                              `json:"type"`
    HelmDeployRequest kogito.RequestData[datamodel.HelmDeployRequest]    `json:"helmDeployRequest"`
    ImagePullSecret   ImagePullSecret                                     `json:"imagePullSecret"`
}

type ImagePullSecret struct {
    URL      string `json:"url"`
    Username string `json:"username"`
    Password string `json:"password"`
}

Custom Scheduler Support

The Package Manager integrates with the Open Cluster Management (OCM) framework for multi-cluster deployments. The scheduler integration is controlled by two fields in the ServiceOrder payload:

  • clusterManager: Set to ocm to activate the OCM path.
  • clusterMetadata: A Base64-encoded YAML manifest that describes workload-to-cluster mapping rules and the target scheduler namespace.

During the parse phase, the Package Manager decodes clusterMetadata, extracts the namespace field, and stores it as the Helm chart annotation sonata.engine/namespace. This annotation is then read by the deploy, activate, and terminate handlers to determine the correct namespace of OCM scheduler, instead of defaulting to the serviceId.

An example clusterMetadata payload (after Base64 decoding) for the HypO scheduler:

apiVersion: scheduling.p2code.eu/v1alpha1
kind: P2CodeSchedulingManifest
metadata:
  name: poc
  namespace: p2code-scheduler-system
spec:
  workloadAnnotations:
    - name: httpd
      annotations:
        - p2code.filter.k8sdistribution=openshift
    - name: nginx
      annotations:
        - p2code.filter.k8sdistribution=kubernetes

The namespace field (p2code-scheduler-system above) becomes the deployment namespace. During termination with OCM, the namespace is intentionally kept alive because the OCM scheduler manages its lifecycle.

Adding Secrets to Vault

Secrets are stored in Vault and retrieved by the Registry API on behalf of the Package Manager.

Two categories exist:

Repository / registry credentials (for private Helm repos, OCI registries, or Git repos):

  1. Navigate to Secrets → secret engine in the Vault UI.
  2. Create a secret at the path matching the repository domain or URL:
  3. OCI registry: registry.example.eu
  4. Helm repo: repo.example.com:8084/helm-repo
  5. Git repo: gitlab.com/api/v4/project
  6. Set key-value pairs: username and password.

Docker image pull secrets (for private container registries):

  1. Navigate to Secrets → secret engine in the Vault UI.
  2. Create a secret using the image pull secret name as the path (e.g., my-app-secret).
  3. Set key-value pairs: url, username, and password.