Skip to content

Wiring upstream CRs ​

The wiring layer lives in internal/wiring/ and owns the translation from Palena CRs → upstream CRs. All external CRs are created as unstructured.Unstructured objects because Palena does not import the upstream operators' Go types — that would explode our module graph and create pinning hell.

Design rules ​

  • Pure functions. Every Build* function takes a Palena CR and returns an *unstructured.Unstructured. No context.Context, no client.Client, no I/O. This makes them trivially testable.
  • omitempty semantics. Optional fields are included in the output map only when the input has a non-nil / non-zero value. This keeps generated specs readable and prevents unnecessary reconciliation loops (empty fields default to upstream defaults).
  • GVK constants. The upstream GroupVersionKind values live in internal/wiring/unstructured.go — a single source of truth if an upstream operator ever bumps its API.

Target GVKs ​

Source CRTarget GVKBuilder
PalenaGatewaypostgresql.cnpg.io/v1 ClusterBuildCNPGCluster
PalenaGatewaylitellm.palena.ai/v1alpha1 LiteLLMInstanceBuildLiteLLMInstance
PalenaModellitellm.palena.ai/v1alpha1 LiteLLMModelBuildLiteLLMModel
PalenaObservabilitylangfuse.palena.ai/v1alpha1 LangfuseInstanceBuildLangfuseInstance

CNPG Cluster ​

internal/wiring/cnpg.go

go
func BuildCNPGCluster(gw *PalenaGateway) *unstructured.Unstructured {
    name := fmt.Sprintf("%s-palena-pg", gw.Name)

    spec := map[string]interface{}{
        "instances": gw.Spec.Database.Managed.Instances,
        "storage": map[string]interface{}{
            "size": gw.Spec.Database.Managed.StorageSize,
        },
        "bootstrap": map[string]interface{}{
            "initdb": map[string]interface{}{
                "database": "litellm",
                "owner":    "litellm",
            },
        },
    }

    if gw.Spec.Database.Managed.StorageClass != nil {
        spec["storage"].(map[string]interface{})["storageClass"] = *gw.Spec.Database.Managed.StorageClass
    }

    if gw.Spec.Database.Managed.PostgreSQL != nil {
        spec["postgresql"] = map[string]interface{}{
            "parameters": gw.Spec.Database.Managed.PostgreSQL.Parameters,
        }
    }

    if gw.Spec.Database.Managed.Backup != nil && gw.Spec.Database.Managed.Backup.Enabled {
        spec["backup"] = map[string]interface{}{
            "barmanObjectStore": /* ... */,
            "retentionPolicy":   gw.Spec.Database.Managed.Backup.RetentionPolicy,
        }
    }

    return buildUnstructured(
        CNPGClusterGVK, name, gw.Namespace, spec,
    )
}

Connection Secret ​

CNPG auto-creates a Secret named <cluster-name>-app with keys: host, port, dbname, user, password, uri, jdbc-uri. The LiteLLMInstance references this Secret; the Palena operator does not create or manage it.

Adding Langfuse's database ​

When a PalenaObservability CR is created, Langfuse needs a second database inside the same cluster. Palena uses CNPG's managed.databases field to add a langfuse database to the existing Cluster without recreating it.

LiteLLMInstance ​

internal/wiring/litellm.go

go
func BuildLiteLLMInstance(gw *PalenaGateway, dbSecretName, redisSecretName string) *unstructured.Unstructured {
    name := fmt.Sprintf("%s-palena-litellm", gw.Name)

    spec := map[string]interface{}{
        "replicas": gw.Spec.Gateway.Replicas,
        "masterKey": map[string]interface{}{
            "secretRef": map[string]interface{}{
                "name": gw.Spec.Gateway.MasterKey.Name,
                "key":  gw.Spec.Gateway.MasterKey.Key,
            },
        },
        "database": map[string]interface{}{
            "cloudnativepg": map[string]interface{}{
                "clusterRef": map[string]interface{}{
                    "name":      fmt.Sprintf("%s-palena-pg", gw.Name),
                    "namespace": gw.Namespace,
                },
                "database": "litellm",
            },
        },
        "redis": map[string]interface{}{
            "external": map[string]interface{}{
                "secretRef": map[string]interface{}{"name": redisSecretName},
                "keys": map[string]interface{}{
                    "host":     "host",
                    "port":     "port",
                    "password": "password",
                },
            },
        },
    }

    // Optional blocks: image, saltKey, configSync, routerSettings,
    // generalSettings, autoscaling, ingress, route, resources, PDB,
    // extraEnvVars — added only if the corresponding spec field is non-nil.

    return buildUnstructured(LiteLLMInstanceGVK, name, gw.Namespace, spec)
}

Every optional field follows the same pattern:

go
if gw.Spec.Gateway.SaltKey != nil {
    spec["saltKey"] = map[string]interface{}{
        "secretRef": map[string]interface{}{
            "name": gw.Spec.Gateway.SaltKey.Name,
            "key":  gw.Spec.Gateway.SaltKey.Key,
        },
    }
}

This keeps the generated spec clean and avoids re-reconciliation loops where the operator would otherwise keep setting an empty field that the upstream controller keeps removing.

LiteLLMModel ​

internal/wiring/litellm_model.go

go
func BuildLiteLLMModel(m *PalenaModel, gatewayName, litellmInstanceName string) *unstructured.Unstructured {
    name := fmt.Sprintf("%s-%s", m.Spec.ModelName, gatewayName)

    params := map[string]interface{}{
        "model": m.Spec.LiteLLMParams.Model,
    }
    if m.Spec.LiteLLMParams.APIBase != "" {
        params["apiBase"] = m.Spec.LiteLLMParams.APIBase
    }
    if m.Spec.LiteLLMParams.APIKeySecretRef != nil {
        params["apiKeySecretRef"] = map[string]interface{}{
            "name": m.Spec.LiteLLMParams.APIKeySecretRef.Name,
            "key":  m.Spec.LiteLLMParams.APIKeySecretRef.Key,
        }
    }
    if m.Spec.LiteLLMParams.RPM != nil { params["rpm"] = *m.Spec.LiteLLMParams.RPM }
    if m.Spec.LiteLLMParams.TPM != nil { params["tpm"] = *m.Spec.LiteLLMParams.TPM }

    spec := map[string]interface{}{
        "instanceRef": map[string]interface{}{"name": litellmInstanceName},
        "modelName":   m.Spec.ModelName,
        "litellmParams": params,
    }
    return buildUnstructured(LiteLLMModelGVK, name, m.Namespace, spec)
}

LangfuseInstance ​

internal/wiring/langfuse.go

go
func BuildLangfuseInstance(obs *PalenaObservability, dbClusterName string) *unstructured.Unstructured {
    name := fmt.Sprintf("%s-palena-langfuse", obs.Name)
    lf := obs.Spec.Langfuse

    spec := map[string]interface{}{
        "web": map[string]interface{}{"replicas": lf.Web.Replicas},
        "database": map[string]interface{}{
            "cloudnativepg": map[string]interface{}{
                "clusterRef": map[string]interface{}{
                    "name":      dbClusterName,
                    "namespace": obs.Namespace,
                },
                "database": "langfuse",
            },
        },
        "blobStorage": buildBlobStorageSpec(lf.BlobStorage),
    }

    if lf.ClickHouse.Managed != nil {
        spec["clickhouse"] = map[string]interface{}{
            "managed": map[string]interface{}{
                "replicas":    lf.ClickHouse.Managed.Replicas,
                "storageSize": lf.ClickHouse.Managed.StorageSize,
            },
        }
    } else if lf.ClickHouse.External != nil {
        spec["clickhouse"] = /* external config */
    }

    // Image, Auth, Ingress, Route, Worker, Resources — same optional pattern.

    return buildUnstructured(LangfuseInstanceGVK, name, obs.Namespace, spec)
}

Langfuse callback wiring ​

internal/wiring/langfuse_callback.go

Once the LangfuseInstance is Ready and its seeded API keys have been stored in a Secret, the observability controller calls:

go
func WireLangfuseCallback(ctx context.Context, c client.Client,
    litellmInstance *unstructured.Unstructured,
    langfuseEndpoint string,
    langfuseAPIKeySecret string,
) error {
    spec := litellmInstance.Object["spec"].(map[string]interface{})

    spec["callbacks"] = map[string]interface{}{
        "langfuse": map[string]interface{}{
            "enabled": true,
            "secretRef": map[string]interface{}{
                "name": langfuseAPIKeySecret,
                "keys": map[string]interface{}{
                    "publicKey": "LANGFUSE_PUBLIC_KEY",
                    "secretKey": "LANGFUSE_SECRET_KEY",
                    "host":      "LANGFUSE_HOST",
                },
            },
        },
    }

    return c.Update(ctx, litellmInstance)
}

Langfuse API key Secret ​

Managed by Palena (owner reference → PalenaObservability):

yaml
apiVersion: v1
kind: Secret
metadata:
  name: <obs-name>-langfuse-apikeys
data:
  LANGFUSE_PUBLIC_KEY: <from langfuse init>
  LANGFUSE_SECRET_KEY: <from langfuse init>
  LANGFUSE_HOST: <langfuse endpoint>

On PalenaObservability deletion, the finalizer removes the callbacks.langfuse block from the LiteLLMInstance before allowing itself to be GC'd — otherwise LiteLLM would keep trying to POST traces to a non-existent endpoint.

Helpers ​

buildUnstructured ​

go
func buildUnstructured(gvk schema.GroupVersionKind, name, namespace string, spec map[string]interface{}) *unstructured.Unstructured {
    obj := &unstructured.Unstructured{}
    obj.SetGroupVersionKind(gvk)
    obj.SetName(name)
    obj.SetNamespace(namespace)
    obj.Object["spec"] = spec
    return obj
}

Reading external CR status ​

go
func getCRStatus(ctx context.Context, c client.Client, gvk schema.GroupVersionKind, name, namespace string) (map[string]interface{}, error) {
    obj := &unstructured.Unstructured{}
    obj.SetGroupVersionKind(gvk)
    if err := c.Get(ctx, client.ObjectKey{Name: name, Namespace: namespace}, obj); err != nil {
        return nil, err
    }
    status, _ := obj.Object["status"].(map[string]interface{})
    return status, nil
}

func isCRReady(status map[string]interface{}) bool {
    conditions, _ := status["conditions"].([]interface{})
    for _, c := range conditions {
        cond, _ := c.(map[string]interface{})
        if cond["type"] == "Ready" && cond["status"] == "True" {
            return true
        }
    }
    return false
}

These helpers are the only place Palena looks into upstream CR status — everywhere else it treats them as opaque blobs.

Released under the Apache 2.0 License. "Palena" is a trademark of bitkaio LLC.