Skip to content

opendefensecloud/ocm-kit

Repository files navigation

ocm-kit

Build status Coverage Status Go Report Card Go Reference GitHub Release

A Go library and CLI tool for working with Open Component Model (OCM) Helm values templates.

Problem Statement

When an OCM ComponentVersion is transferred from one OCI Registry to another, the default values of Helm Charts will contain images of the old OCI Registry. This library provides functionality to manage and render Helm values templates that are embedded as resources in OCM components, ensuring that image references are correctly resolved regardless of the registry they're accessed from.

Solution Overview

The library provides mechanisms to:

  1. Find Helm Values Templates: Locate Helm values templates in OCM components using a label-based approach (opendefense.cloud/helm/values-for)
  2. Render Templates: Process the templates using Go's text/template with sprig functions for flexible value substitution
  3. Extract Component Data: Automatically extract resource information from OCM components and prepare it for templating

For a smoother development experience, a small ocm-kit CLI is also provided, which allows local testing of rendering helm values templates or verifying outputs.

Installation & Building

Prerequisites

  • Go 1.25.7 or later
  • Docker (for running e2e tests)
  • OCM CLI (for e2e tests)

Build

go build ./...

Run Go tests

# Run all tests
make test
# Run particular tests
go test ./helmvalues

Run e2e Tests

# Run e2e tests with default version (timestamp-based)
make e2e

# Run e2e tests with a stable component version
VERSION=0.1.0 make e2e

# Run e2e tests but keep zot registry running
make e2e-keep-zot

# Stop and remove zot registry
make e2e-stop-zot

CLI Usage

The ocm-kit command-line tool renders Helm values templates from OCM components.

Basic Usage

ocm-kit <component-version-ref> [flags]

Where <component-version-ref> is in the format: protocol://host/namespace//component:version

Examples:

# Render first values template from component in local OCI registry
ocm-kit "http://localhost:5000/my-components//opendefense.cloud/arc:0.1.0"

# Render specific values template from remote registry by providing which helm resource it is for
ocm-kit "https://registry.example.com/stable//example.com/myapp:1.2.3" -r my-chart

# Use a local template file instead of component template
ocm-kit "http://localhost:5000/my-components//opendefense.cloud/arc:0.1.0" \
  --local-helm-values-template ./values.yaml.tpl

Command Flags

  • -r, --chart-resource string - Name of the Helm chart resource in the component (default: "")
  • -f, --local-helm-values-template string - Path to a local Helm values template file (overrides component template)
  • -p, --pull-secrets-file string - Path to a pull secrets JSON file mapping registries to Kubernetes secret names
  • -h, --help - Display help message

Registry Credentials

ocm-kit reuses OCM's standard credential resolution. On startup it calls ocmutils.Configure, which loads $HOME/.ocmconfig if present. Place your registry credentials in that file and they are picked up transparently — there are no CLI flags or environment variables for credentials.

A minimal config that authenticates against a single OCI registry:

type: generic.config.ocm.software/v1
configurations:
  - type: credentials.config.ocm.software
    consumers:
      - identity:
          type: OCIRegistry
          hostname: ghcr.io
          pathprefix: opendefensecloud
        credentials:
          - type: Credentials/v1
            properties:
              username: <user>
              password: <token>

identity selects which requests the credentials apply to. hostname is matched exactly; pathprefix matches by path components (the most specific prefix wins). scheme (http/https) and port are optional — leaving them out matches any scheme/port on the host. This is how you scope different tokens to different namespaces on the same registry.

For a local insecure registry:

type: generic.config.ocm.software/v1
configurations:
  - type: credentials.config.ocm.software
    consumers:
      - identity:
          type: OCIRegistry
          scheme: http
          hostname: 127.0.0.1
          port: 5000
        credentials:
          - type: Credentials/v1
            properties:
              username: admin
              password: admin

To reuse credentials you have already configured for Docker, point OCM at ~/.docker/config.json instead of duplicating them. With propagateConsumerIdentity: true, every entry in the docker config is exposed as an OCIRegistry consumer:

type: generic.config.ocm.software/v1
configurations:
  - type: credentials.config.ocm.software
    repositories:
      - repository:
          type: DockerConfig/v1
          dockerConfigFile: "~/.docker/config.json"
          propagateConsumerIdentity: true

repositories and consumers can be combined in the same configuration — explicit consumers take precedence over what is propagated from the docker config, so you can override individual hosts when needed.

See the OCM credentials tutorial for the full set of identity types and credential providers.

Pull Secrets

When rendering the Helm values template, you may need to attach imagePullSecrets to deployments. The --pull-secrets-file flag and the pullSecretFor template function work together to map OCI registries to Kubernetes Secret names.

This approach allows the template author to specify where to add imagePullSecrets in a values.yaml while the deployer has control over deployment specific data like the concrete secret names.

The pull secrets file uses the following format:

{
  "$schema": "https://raw.githubusercontent.com/opendefensecloud/ocm-kit/refs/heads/main/helmvalues/pullsecrets-schema.json",
  "pullSecrets": [
    {
      "registry": "docker.io",
      "secretName": "docker-hub-cred"
    },
    {
      "registry": "ghcr.io/my-org",
      "secretName": "ghcr-org-cred"
    },
    {
      "registry": "localhost:5000",
      "secretName": "regcred"
    }
  ]
}

The pullSecretFor function in templates resolves an OCI reference (hostname, hostname/repo, or full image ref) to the matching secret name. Resolution walks from most-specific to least-specific path segments:

  • pullSecretFor "ghcr.io/my-org/my-repo:latest" checks ghcr.io/my-org/my-repo -> ghcr.io/my-org -> ghcr.io
  • pullSecretFor "docker.io" checks the registry host directly

If no match is found pullSecretFor returns the empty string.

It is advised that templates may always be written in a way that gracefully handle pullSecretFor returning an empty string value. Like in the example below, template authors can make use of go-template's with expression.

Example template usage:

{{- $image := index .OCIResources "my-image" }}
{{- with pullSecretFor $image.Host }}
imagePullSecrets:
  - name: {{ . }}
{{- end }}

CLI invocation:

ocm-kit "https://example.com/my-components//example.com/my-component:0.1.0" \
  --pull-secrets-file ./pull-secrets.json

Example

Values Template

apiserver:
  image:
    {{- $apiserver := index .OCIResources "arc-apiserver-image" }}
    repository: {{ $apiserver.Host }}/{{ $apiserver.Repository }}
    tag: {{ $apiserver.Tag }}

controller:
  image:
    {{- $controller := index .OCIResources "arc-controller-manager-image" }}
    repository: {{ $controller.Host }}/{{ $controller.Repository }}
    tag: {{ $controller.Tag }}

etcd:
  image:
    {{- $etcdImage := index .OCIResources "etcd-image" }}
    repository: {{ $etcdImage.Host }}/{{ $etcdImage.Repository }}
    tag: {{ $etcdImage.Tag }}

Render Output

apiserver:
  image:
    repository: localhost:5000/my-components/opendefensecloud/arc-apiserver
    tag: v0.2.0

controller:
  image:
    repository: localhost:5000/my-components/opendefensecloud/arc-controller-manager
    tag: v0.2.0

Library Usage Example

package main

import (
    "context"
    "fmt"
    "log"

    "go.opendefense.cloud/ocm-kit/helmvalues"
    "go.opendefense.cloud/ocm-kit/compver"
    "ocm.software/ocm/api/ocm"
    "ocm.software/ocm/api/ocm/extensions/repositories/ocireg"
)

func main() {
    ctx := context.Background()

    // Parse component version reference
    cvr, err := compver.SplitRef("http://localhost:5000/my-components//opendefense.cloud/arc:0.1.0")
    if err != nil {
        log.Fatal(err)
    }

    // Setup OCM repository
    octx := ocm.FromContext(ctx)
    repo, err := octx.RepositoryForSpec(ocireg.NewRepositorySpec(cvr.BaseURL()))
    if err != nil {
        log.Fatal(err)
    }
    defer repo.Close()

    // Get component version
    compVer, err := repo.LookupComponentVersion(cvr.ComponentName, cvr.Version)
    if err != nil {
        log.Fatal(err)
    }
    defer compVer.Close()

    // Find helm values template for a specific chart
    tmpl, err := helmvalues.GetHelmValuesTemplate(compVer, "helm-chart")
    if err != nil {
        log.Fatal(err)
    }

    // Get rendering input with component data
    input, err := helmvalues.GetRenderingInput(compVer)
    if err != nil {
        log.Fatal(err)
    }

    // Render the template
    renderedValues, err := helmvalues.Render(tmpl, input)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(renderedValues)
}

Template Variables

When rendering templates, the following data is available via the context:

.OCIResources

A map of all oci resources in the component by resource name. Only resources with an OCI-based access method are listed:

Each resource is automatically parsed into an object with:

  • .Host - The registry host
  • .Repository - The repository path
  • .Tag - The image tag
  • .Digest - The image digest

For other access methods (OCI blobs, local blobs, S3, Git, etc.), the relevant fields are extracted into structured maps.

Access example:

{{- $image := index .OCIresources "my-image" }}
repository: {{ $image.host }}/{{ $image.repository }}
tag: {{ $image.tag }}

.PullSecrets

A map of registries to secret-names. Use the pullSecretFor template function to refer to possible imagePullSecrets. It implements a hierarchical path-resolution logic (See Pull Secrets).

.Component

Component metadata available as a compdesc.ComponentSpec, providing access to:

  • Component name and version
  • Provider information
  • Resources list
  • Sources
  • References
  • Repository contexts

Resource Labeling

Helm values templates should be labeled in the OCM component descriptor with:

labels:
  - name: opendefense.cloud/values-for
    value: <helm-chart-resource-name>

This label indicates which Helm chart resource this template is for.

Dependencies

  • github.com/Masterminds/sprig/v3 - Template functions
  • github.com/mandelsoft/vfs - Virtual filesystem handling
  • ocm.software/ocm - OCM API

License

See LICENSE file

About

Helper functions and additional functionality for working with OCM components

Resources

License

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors