Skip to content

Kubernetes-Interacting Packages

Reference documentation for the internal/common/ packages that interact with the Kubernetes API server (CC-0005). These packages provide reconciler building blocks for managing external operator CRDs (MariaDB, External Secrets Operator, cert-manager) and core Kubernetes resources (Deployments, Services, Jobs, CronJobs, ConfigMaps).

All packages share these conventions:

  • Idempotent create-or-updateEnsure* functions create a resource if it does not exist or update its spec if it already exists. They use Get + Create/Update (not controllerutil.CreateOrUpdate) for explicit control.
  • Owner referencescontrollerutil.SetControllerReference is called on create so the resource is garbage-collected when the owning CR is deleted.
  • Readiness reporting — Functions that create resources return (bool, error), where true means the resource is ready and false means it exists but is not yet ready.
  • Error wrapping — All errors include context via fmt.Errorf with %w for errors.Is/errors.As compatibility.

Import Paths

PackageImport Path
configgithub.com/c5c3/forge/internal/common/config
databasegithub.com/c5c3/forge/internal/common/database
deploymentgithub.com/c5c3/forge/internal/common/deployment
jobgithub.com/c5c3/forge/internal/common/job
policygithub.com/c5c3/forge/internal/common/policy
secretsgithub.com/c5c3/forge/internal/common/secrets
tlsgithub.com/c5c3/forge/internal/common/tls

External CRD Dependencies

These packages import typed Go structs from external operator modules:

OperatorGo ModuleAPI VersionTypes Used
mariadb-operatorgithub.com/mariadb-operator/mariadb-operatorv1alpha1Database, User, Grant
External Secrets Operatorgithub.com/external-secrets/external-secretsv1beta1 (ExternalSecret), v1alpha1 (PushSecret)ExternalSecret, PushSecret
cert-managergithub.com/cert-manager/cert-managerv1Certificate

Note: The PushSecret API is v1alpha1 (unstable). Its schema may change in future ESO releases. Pin the ESO module version in go.mod to avoid breakage.


Package: config

Implements the INI configuration rendering pipeline for CobaltCore operators (CC-0004). CC-0005 adds the CreateImmutableConfigMap function for Kubernetes-interacting config management.

CreateImmutableConfigMap

go
func CreateImmutableConfigMap(
    ctx context.Context,
    c client.Client,
    scheme *runtime.Scheme,
    owner client.Object,
    baseName, namespace string,
    data map[string]string,
) (string, error)

Creates an immutable ConfigMap with a content-hash suffix appended to the base name. The hash ensures that configuration changes result in new ConfigMap names, triggering pod restarts when the ConfigMap is referenced in a Deployment's volume spec.

Parameters:

NameTypeDescription
ctxcontext.ContextRequest context
cclient.ClientKubernetes API client
scheme*runtime.SchemeScheme for owner reference resolution
ownerclient.ObjectOwning CR for garbage collection
baseNamestringBase name for the ConfigMap (hash is appended as -<hash>)
namespacestringNamespace for the ConfigMap
datamap[string]stringConfigMap data entries

Returns:

ValueDescription
stringActual ConfigMap name including the 8-character SHA256 hash suffix
errorNon-nil on owner reference or API server failure

Behavior:

  • Computes a deterministic SHA256 hash from sorted data keys and values.
  • Truncates the hash to 8 hex characters and appends it as baseName-<hash>.
  • Sets Immutable: true on the ConfigMap.
  • Sets a controller owner reference on the ConfigMap.
  • If a ConfigMap with the same name already exists (AlreadyExists error), returns the name without error (idempotent).
  • Same data always produces the same hash (deterministic).
  • Different data always produces a different hash.

Example:

go
name, err := config.CreateImmutableConfigMap(ctx, client, scheme, owner,
    "keystone-config", "openstack",
    map[string]string{"keystone.conf": renderedINI},
)
// name == "keystone-config-a1b2c3d4"

Package: database

Manages MariaDB database resources for CobaltCore operators. Uses typed structs from github.com/mariadb-operator/mariadb-operator/api/v1alpha1.

EnsureDatabase

go
func EnsureDatabase(
    ctx context.Context,
    c client.Client,
    scheme *runtime.Scheme,
    owner client.Object,
    db *mariadbv1alpha1.Database,
) (bool, error)

Creates or updates a MariaDB Database CR.

Returns: (true, nil) when the Database has a Ready condition with status True; (false, nil) when it exists but is not yet ready; (false, error) on failure.

Behavior:

  • On create: sets controller owner reference, creates the resource, returns (false, nil).
  • On update: overwrites existing.Spec with the provided spec.
  • Readiness is determined by IsDatabaseReady on the existing resource.

IsDatabaseReady

go
func IsDatabaseReady(db *mariadbv1alpha1.Database) bool

Pure function. Returns true if the Database has a Ready condition with status True. Uses meta.IsStatusConditionTrue from k8s.io/apimachinery.

EnsureDatabaseUser

go
func EnsureDatabaseUser(
    ctx context.Context,
    c client.Client,
    scheme *runtime.Scheme,
    owner client.Object,
    user *mariadbv1alpha1.User,
    grant *mariadbv1alpha1.Grant,
) (bool, error)

Creates or updates a MariaDB User CR and a Grant CR in a single call.

Returns: (true, nil) when both User and Grant have Ready conditions with status True; (false, nil) when either is not yet ready; (false, error) on failure.

Behavior:

  • Processes User first, then Grant. If User creation/update fails, Grant is not attempted.
  • Both resources receive controller owner references.

IsUserReady

go
func IsUserReady(user *mariadbv1alpha1.User) bool

Pure function. Returns true if the User has a Ready condition with status True.

IsGrantReady

go
func IsGrantReady(grant *mariadbv1alpha1.Grant) bool

Pure function. Returns true if the Grant has a Ready condition with status True.

RunDBSyncJob

go
func RunDBSyncJob(
    ctx context.Context,
    c client.Client,
    scheme *runtime.Scheme,
    owner client.Object,
    syncJob *batchv1.Job,
) (bool, error)

Creates a database synchronization Job if it does not already exist and reports completion status. Delegates directly to job.RunJob.

Returns: (true, nil) when the Job has completed; (false, nil) when still running; (false, error) on failure.


Package: deployment

Manages Kubernetes Deployments and Services for CobaltCore operators.

EnsureDeployment

go
func EnsureDeployment(
    ctx context.Context,
    c client.Client,
    scheme *runtime.Scheme,
    owner client.Object,
    deploy *appsv1.Deployment,
) (bool, error)

Creates or updates a Deployment.

Returns: (true, nil) when all replicas are available; (false, nil) when the Deployment exists but is not yet ready; (false, error) on failure.

Behavior:

  • On create: sets controller owner reference, creates the resource, returns (false, nil).
  • On update: overwrites existing.Spec with the provided spec.
  • Readiness is determined by IsDeploymentReady on the existing resource.

EnsureService

go
func EnsureService(
    ctx context.Context,
    c client.Client,
    scheme *runtime.Scheme,
    owner client.Object,
    svc *corev1.Service,
) error

Creates or updates a Service. Does not report readiness (Services are ready immediately).

Behavior:

  • On create: sets controller owner reference, creates the resource.
  • On update: preserves the existing ClusterIP and ClusterIPs values assigned by the API server before overwriting the spec. This prevents accidental ClusterIP reassignment, which would break existing DNS-based service discovery.

IsDeploymentReady

go
func IsDeploymentReady(deploy *appsv1.Deployment) bool

Pure function. Returns true when both conditions are met:

  1. deploy.Status.ReadyReplicas >= *deploy.Spec.Replicas (defaults to 1 if Spec.Replicas is nil).
  2. The Deployment has an Available condition with status True.

Edge cases:

ScenarioResult
Spec.Replicas is nilDefaults to 1
No Available conditionfalse
ReadyReplicas < desiredfalse
ReadyReplicas >= desired and Available=Truetrue

Package: job

Manages Kubernetes Jobs and CronJobs for CobaltCore operators.

RunJob

go
func RunJob(
    ctx context.Context,
    c client.Client,
    scheme *runtime.Scheme,
    owner client.Object,
    job *batchv1.Job,
) (bool, error)

Creates a Job if it does not already exist and reports completion status.

Returns: (true, nil) when the Job has a Complete condition with status True; (false, nil) when the Job exists but is still running; (false, error) when the Job has permanently failed (e.g. exceeded backoffLimit); (false, error) on unexpected API failures.

Behavior:

  • If the Job does not exist: creates it with a controller owner reference, returns (false, nil) (newly created Jobs are never immediately complete).
  • If the Job already exists: first checks for permanent failure via IsJobFailed (returns an error to prevent infinite requeue loops), then checks completion via IsJobComplete. Jobs are immutable after creation — they are not updated.
  • Reconcilers should call RunJob on each reconciliation loop. The function is idempotent: calling it when the Job already exists and is complete returns (true, nil) without side effects.

EnsureCronJob

go
func EnsureCronJob(
    ctx context.Context,
    c client.Client,
    scheme *runtime.Scheme,
    owner client.Object,
    cronJob *batchv1.CronJob,
) error

Creates or updates a CronJob with a controller owner reference.

Behavior:

  • On create: sets controller owner reference, creates the resource.
  • On update: overwrites existing.Spec with the provided spec. CronJob spec updates take effect on the next scheduled run.

IsJobComplete

go
func IsJobComplete(job *batchv1.Job) bool

Pure function. Returns true if the Job has a Complete condition with status True.

Edge cases:

ScenarioResult
No conditionsfalse
Failed condition onlyfalse
Complete condition with status Falsefalse
Complete condition with status Truetrue

IsJobFailed

go
func IsJobFailed(job *batchv1.Job) bool

Pure function. Returns true if the Job has a Failed condition with status True, indicating the Job has permanently failed (e.g. exceeded its backoffLimit).

Edge cases:

ScenarioResult
No conditionsfalse
Complete condition onlyfalse
Failed condition with status Falsefalse
Failed condition with status Truetrue

Package: policy

Provides pure functions for OpenStack oslo.policy rule rendering, merging, and validation (CC-0004). CC-0005 adds the LoadPolicyFromConfigMap function for reading policy from Kubernetes ConfigMaps.

LoadPolicyFromConfigMap

go
func LoadPolicyFromConfigMap(
    ctx context.Context,
    c client.Client,
    key client.ObjectKey,
) (map[string]string, error)

Reads a ConfigMap by namespace/name and extracts the policy.yaml key as a map[string]string of oslo.policy rules.

Parameters:

NameTypeDescription
ctxcontext.ContextRequest context
cclient.ClientKubernetes API client
keyclient.ObjectKeyNamespace and name of the ConfigMap

Returns:

ValueDescription
map[string]stringParsed policy rules (action → rule expression)
errorNon-nil when ConfigMap is missing, key is absent, or YAML is invalid

Error conditions:

ConditionError
ConfigMap does not existWrapped API server error (compatible with apierrors.IsNotFound)
policy.yaml key absentConfigMap <key> does not contain key "policy.yaml"
Invalid YAML contentparsing policy.yaml from ConfigMap <key>: <parse error>

Example:

go
rules, err := policy.LoadPolicyFromConfigMap(ctx, client,
    types.NamespacedName{Namespace: "openstack", Name: "keystone-policy"},
)
// rules == map[string]string{"identity:get_user": "role:admin", ...}

Package: secrets

Manages External Secrets Operator resources and Kubernetes Secrets for CobaltCore operators. Uses typed structs from github.com/external-secrets/external-secrets.

WaitForExternalSecret

go
func WaitForExternalSecret(
    ctx context.Context,
    c client.Client,
    key client.ObjectKey,
) (bool, error)

Checks whether the ExternalSecret identified by key has a Ready condition with status True (the ESO ExternalSecretReady condition type).

Returns: (true, nil) when synced; (false, nil) when not yet synced; (false, error) when the ExternalSecret does not exist or API call fails.

Behavior:

  • This is a point-in-time check, not a blocking wait. Reconcilers should call it on each reconciliation loop and requeue if it returns false.
  • Uses the ESO ExternalSecretReady condition type constant, not a raw string.

IsSecretReady

go
func IsSecretReady(
    ctx context.Context,
    c client.Client,
    key client.ObjectKey,
    expectedKeys ...string,
) (bool, error)

Checks whether a Kubernetes Secret exists at the given key and, when expectedKeys are provided, verifies that all specified keys are present in the Secret's .Data field.

Returns: (true, nil) if the Secret exists and contains all expected keys; (false, nil) if not found or missing expected keys; (false, error) on unexpected API failures.

Behavior:

  • A NotFound error is treated as a normal condition ((false, nil)), not a failure.
  • When no expectedKeys are provided, only checks for Secret existence.
  • When expectedKeys are provided, returns (false, nil) if any key is absent from Secret.Data.

GetSecretValue

go
func GetSecretValue(
    ctx context.Context,
    c client.Client,
    key client.ObjectKey,
    dataKey string,
) (string, error)

Retrieves and decodes the value of a specific data key from a Secret.

Parameters:

NameTypeDescription
keyclient.ObjectKeyNamespace and name of the Secret
dataKeystringKey within Secret.Data to retrieve

Returns: The decoded string value, or an error if the Secret or key is not found.

Error conditions:

ConditionError
Secret does not existWrapped API server error
Key not in Secret.Datakey "<dataKey>" not found in Secret <namespace>/<name>

EnsurePushSecret

go
func EnsurePushSecret(
    ctx context.Context,
    c client.Client,
    scheme *runtime.Scheme,
    owner client.Object,
    ps *esov1alpha1.PushSecret,
) error

Creates or updates a PushSecret CR with a controller owner reference.

Behavior:

  • On create: sets controller owner reference via SetControllerReference, creates the resource.
  • On update: overwrites existing.Spec with the provided spec.
  • Uses the ESO v1alpha1 PushSecret API (unstable — see External CRD Dependencies).

Package: tls

Manages TLS certificates and secrets for CobaltCore operators. Uses typed structs from github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1.

EnsureCertificate

go
func EnsureCertificate(
    ctx context.Context,
    c client.Client,
    scheme *runtime.Scheme,
    owner client.Object,
    cert *certmanagerv1.Certificate,
) (bool, error)

Creates or updates a cert-manager Certificate CR.

Returns: (true, nil) when the Certificate has a Ready condition with status True; (false, nil) when it exists but is not yet ready; (false, error) on failure.

Behavior:

  • On create: sets controller owner reference, creates the resource, returns (false, nil).
  • On update: overwrites existing.Spec with the provided spec.
  • Readiness is determined by IsCertificateReady on the existing resource.
  • cert-manager creates a Secret with the TLS certificate once the Certificate is ready. Use GetTLSSecret to retrieve it.

IsCertificateReady

go
func IsCertificateReady(cert *certmanagerv1.Certificate) bool

Pure function. Returns true if the Certificate has a Ready condition (CertificateConditionReady) with status True (cmmeta.ConditionTrue).

GetTLSSecret

go
func GetTLSSecret(
    ctx context.Context,
    c client.Client,
    key client.ObjectKey,
) (*corev1.Secret, error)

Retrieves a Secret by key. Intended for obtaining TLS secrets created by cert-manager.

Returns: The Secret object, or an error if the Secret does not exist.

Behavior:

  • Returns the full *corev1.Secret including Data with tls.crt and tls.key entries (when created by cert-manager).
  • Returns a wrapped error on NotFound — callers can check with apierrors.IsNotFound(err).

Cross-Package Dependencies

text
database/ ──depends-on──▶ job/

The database package imports job to delegate RunDBSyncJob to job.RunJob. All other CC-0005 packages are independent of each other.

Reconciler Integration Pattern

A typical reconciler calls these packages in its sub-reconciler phases:

text
SecretsReady      → secrets.WaitForExternalSecret, secrets.IsSecretReady
DatabaseReady     → database.EnsureDatabase, database.EnsureDatabaseUser,
                    database.RunDBSyncJob
ConfigReady       → config.CreateImmutableConfigMap
DeploymentReady   → deployment.EnsureDeployment, deployment.EnsureService
TLSReady          → tls.EnsureCertificate, tls.GetTLSSecret
PolicyReady       → policy.LoadPolicyFromConfigMap

Each phase returns a readiness boolean. The reconciler advances to the next phase only when the previous phase returns true. If any phase returns false, the reconciler requeues and re-evaluates on the next reconciliation.