note

package
v0.4.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 14, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

README

pkg/note

Notes are pure transformation functions available in every Orkestra template expression — status.fields, normalize.spec, onCreate, onReconcile, mutation rules, validation rules, and conversion paths.

The name is intentional. In music, notes are the atomic units from which everything is composed — precise, combinable, universally understood. In Orkestra, notes serve the same role.

What a note is

  • Pure — same input always produces the same output
  • Safe — handles empty/nil input without panicking
  • Stateless — no I/O, no external calls, no shared state

Notes are not hooks. Hooks are for external API calls and side effects. Notes are for data transformation.

Where notes work

Every {{ }} expression in any Katalog field:

normalize:
  spec:
    schedule: "{{ cronFromMap .spec.schedule }}"

status:
  fields:
    - path: phase
      value: "{{ boolTernary .spec.suspend \"Suspended\" \"Active\" }}"

onCreate:
  secrets:
    - name: "{{ .metadata.name }}-creds"
      once: true
      data:
        password: "{{ randomAlphanumeric 32 }}"

Developer documentation

Complete documentation is in docs/.

I want to… Go to
Work with strings 01 — String Notes
Do arithmetic on spec fields 02 — Math Notes
Express conditional values 03 — Conditional Notes
Inspect or convert field types 04 — Type Notes
Work with cron expressions 05 — Cron Notes
Generate secrets 06 — Random Notes
Work with lists and maps 07 — Collection Notes
Access fields safely with defaults 08 — Safe Access Notes
Navigate child/cross-CRD objects 09 — Kubernetes Notes
Read container images and env vars 10 — Container Notes

Adding a new note

1. Implement

  • Identify the domain (cron, strings, math, types, conditional, kubernetes, replica, container, job, service, …)
  • Add the function to the appropriate *.go file
  • Register it in that file's xxxNotes() function — it is automatically included via note.Map()
  • Document it in the corresponding docs/ file

Contract: handle empty/nil input with a safe zero value, not a panic. Return (value, error) for functions that can meaningfully fail; return just value for infallible ones.

2. Unit test

Write a test in the corresponding *_test.go file. Cover the nil/empty path, the happy path, and any edge cases. For most note families (strings, math, conditional, types, cron, random, collections) this is sufficient — the note is done.

3. Kubernetes-family notes also require a katalog entry

Notes in kubernetes.go, kube_replica.go, kube_container.go, kube_job.go, and kube_service.go take live Kubernetes objects as input (map[string]interface{} from the dynamic client). Unit tests use hand-crafted maps — they cannot cover missing fields, null values, or schema variations that only appear with real API responses. The note katalog closes that gap.

Add a status.fields entry to fixture/katalog.yaml:

- path: myNewNote
  value: "{{ myNewNote .children.deployment }}"

Then verify it against a live cluster:

kubectl apply -f pkg/note/fixture/crd.yaml
ork run -f pkg/note/fixture/katalog.yaml   # keep running in one terminal

kubectl apply -f pkg/note/fixture/cr.yaml
kubectl get noteprobe my-probe -o yaml -w  # watch until phase: Ready

Confirm the field appears with the expected value. Clean up when done:

cd pkg/note/fixture && bash cleanup.sh

The fixture/ katalog is the living e2e test for the kubernetes note family. It runs in CI on every change to kube_*.go or kubernetes.go.

Documentation

Overview

pkg/note/git_docker.go

Git and Docker notes for use in Katalog templates.

These notes work in two directions:

  1. Input to git:/docker: blocks — constructing the values those blocks need.
  2. Output from git:/docker: blocks — transforming .git.* and .docker.* context.

Git context injected by the git: block:

.git.commit       — full commit SHA
.git.changed      — "true" / "false"
.git.path         — local clone path
.git.branch       — resolved branch name
.git.error        — non-empty on failure (when continueOnError: true)
.git.called       — "true" when the block ran

Docker context injected by the docker: block:

.docker.image     — fully qualified image reference (registry/repo:tag)
.docker.digest    — image digest after push (sha256:abc...)
.docker.buildSucceeded  — "true" / "false"
.docker.error           — non-empty on failure
.docker.called          — "true" when the block ran

Example — image with commit-pinned tag:

docker:
  image: "{{ .spec.registry }}/{{ repoName .spec.repo }}:{{ gitShortCommit .git.commit }}"

Example — only deploy when git changed and build succeeded:

deployments:
  - name: "{{ .metadata.name }}"
    image: "{{ dockerWithDigest .docker.image .docker.digest }}"
    when:
      - field: git.changed
        equals: "true"
      - field: docker.buildSucceeded
        equals: "true"

Package note provides the Orkestra template function library.

A note is a pure, named transformation function available in every template expression across the Orkestra runtime — conversion paths, status fields, mutation rules, when conditions, onCreate templates.

The name aligns with Orkestra's musical identity. Notes are the atomic units from which music is composed. In Orkestra, notes are the atomic units from which declarative operator behavior is composed — small, precise, and available everywhere a template expression is evaluated.

Notes are pure functions. They receive values and return transformed values. They cannot perform I/O, call external APIs, or produce side effects. For those, use Go hooks.

Usage in a Katalog:

conversion:
  paths:
    - from: v1
      to: v2
      spec:
        schedule: "{{ cronToMap .spec.schedule }}"

status:
  fields:
    - path: environment
      value: "{{ toLower .spec.environment }}"
    - path: replicas
      value: "{{ default .spec.replicas 2 }}"
    - path: schedule
      value: "{{ cronFromAny .spec.schedule }}"

pkg/note/random.go

Random generation notes — for use with once: true on secrets.

IMPORTANT: These notes are pure in the mathematical sense — they produce random output. They are NOT idempotent. They MUST only be used in template sources that declare once: true (SecretTemplateSource.Once), which ensures the template is only evaluated once (when the Secret does not yet exist).

Using random notes without once: true causes a new value to be generated on every reconcile cycle — passwords change every 30 seconds, breaking every application that uses them.

Correct usage:

secrets:
  - name: "{{ .metadata.name }}-credentials"
    once: true                              # ← REQUIRED with random notes
    data:
      password: "{{ randomAlphanumeric 32 }}"
      apiKey:   "{{ randomHex 16 }}"

The notes are registered in note.Map() and available in all template expressions — the once: guard is the semantic safeguard, not a code restriction.

Index

Constants

View Source
const CronMapSentinel = "\x00CMAP\x00"

CronMapSentinel is a prefix written by the cronToMap template function to signal that the resolved string is a JSON-encoded cron map, not a plain string. Detected by pkg/webhook/conversion_logic.resolveValue, which parses it back to map[string]interface{} so it lands as a nested object in the converted spec — enabling {{ cronToMap .spec.schedule }} as a one-liner shorthand for the v1 → v2 conversion path.

Null bytes are used because they cannot appear in normal YAML field values.

Variables

View Source
var BuiltinNotes = []NoteInfo{}/* 118 elements not displayed */

BuiltinNotes is the complete registry of documented Orkestra note functions. Generated from pkg/note/docs/ — run `make generate-notes` to refresh.

Functions

func CronToMap added in v0.3.1

func CronToMap(v interface{}) map[string]interface{}

CronToMap is the Go API for splitting a cron expression into its five named fields. Accepts a string or an existing map (returned as-is).

func Domains added in v0.3.2

func Domains() []string

Domains returns the distinct domain values present in BuiltinNotes, in the order they first appear.

func Map

func Map() template.FuncMap

Map returns the complete Orkestra note library as a template.FuncMap. The map is built once and returned on every call — no allocation overhead.

Integrate into the resolver:

tmpl, err := template.New("f").
    Option("missingkey=zero").
    Funcs(note.Map()).
    Parse(value)

func OrkLen

func OrkLen(v interface{}) int

OrkLen returns the length of a string, slice, or map. Named orkLen to avoid shadowing Go's built-in len. Registered in note.Map() as "len" since Go templates do not have a built-in len that handles all three types uniformly.

Template: {{ len .spec.regions }} → 3 (slice with 3 elements) Template: {{ len .spec.schedule }} → 5 (map with 5 fields) Template: {{ len .metadata.name }} → 12 (string length)

func TypeOf

func TypeOf(v interface{}) string

typeOf — returns the runtime type name of any value. orkLen — returns the length of a string, slice, or map.

typeOf is used in:

  • Template expressions: {{ typeOf .spec.schedule }} → "string" or "map"
  • when: conditions: operator: typeOf, value: map

The condition operator path uses NavigateRawPath → note.TypeOf directly. The template expression path uses the FuncMap entry registered in note.Map(). Both must return the same strings for the Katalog to be predictable.

Type strings (match Python/JavaScript convention for familiarity):

"string"  → Go string
"number"  → Go float64, int64, int (JSON numbers come back as float64)
"bool"    → Go bool
"map"     → Go map[string]interface{} (YAML objects, structured fields)
"slice"   → Go []interface{} (YAML arrays, list fields)
"null"    → nil
"unknown" → any other type

YAML type behaviour:

spec:
  schedule: "*/5 * * * *"          → typeOf returns "string"
  schedule:                          → typeOf returns "map"
    minute: "*/5"
  regions: [us-east-1, eu-west-1]   → typeOf returns "slice"
  replicas: 3                        → typeOf returns "number"
  enabled: true                      → typeOf returns "bool"

TypeOf returns the type name of any interface{} value. Exported so pkg/types/when.go can call it from EvaluateOneCond without the template engine being involved.

Types

type NoteInfo added in v0.3.2

type NoteInfo struct {
	Name        string   // template function name, e.g. "cronToMap"
	Domain      string   // note family, e.g. "cron", "strings", "kubernetes"
	Description string   // one-line summary
	Example     string   // short usage snippet (optional)
	SeeAlso     []string // related note names
	Keywords    []string // search tags parsed from the "Keywords:" line in the doc
}

NoteInfo holds discoverable metadata for one note function. Populated from BuiltinNotes (catalog_generated.go) which is produced by make generate-notes — do not edit catalog_generated.go directly.

func GetNote added in v0.3.2

func GetNote(name string) (NoteInfo, bool)

GetNote returns the NoteInfo for the given name (case-insensitive). Returns the zero value and false when not found.

func ListByDomain added in v0.3.2

func ListByDomain(domain string) []NoteInfo

ListByDomain returns notes whose Domain matches the given value (case-insensitive).

func ListNotes added in v0.3.2

func ListNotes() []NoteInfo

ListNotes returns all built-in notes sorted by domain then name.

func SearchNotes added in v0.3.2

func SearchNotes(term string) []NoteInfo

SearchNotes returns notes whose Name, Description, Example, or Keywords contains term (case-insensitive).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL