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-update —
Ensure*functions create a resource if it does not exist or update its spec if it already exists. They useGet+Create/Update(notcontrollerutil.CreateOrUpdate) for explicit control. - Owner references —
controllerutil.SetControllerReferenceis called on create so the resource is garbage-collected when the owning CR is deleted. - Readiness reporting — Functions that create resources return
(bool, error), wheretruemeans the resource is ready andfalsemeans it exists but is not yet ready. - Error wrapping — All errors include context via
fmt.Errorfwith%wforerrors.Is/errors.Ascompatibility.
Import Paths
| Package | Import Path |
|---|---|
config | github.com/c5c3/forge/internal/common/config |
database | github.com/c5c3/forge/internal/common/database |
deployment | github.com/c5c3/forge/internal/common/deployment |
job | github.com/c5c3/forge/internal/common/job |
policy | github.com/c5c3/forge/internal/common/policy |
secrets | github.com/c5c3/forge/internal/common/secrets |
tls | github.com/c5c3/forge/internal/common/tls |
External CRD Dependencies
These packages import typed Go structs from external operator modules:
| Operator | Go Module | API Version | Types Used |
|---|---|---|---|
| mariadb-operator | github.com/mariadb-operator/mariadb-operator | v1alpha1 | Database, User, Grant |
| External Secrets Operator | github.com/external-secrets/external-secrets | v1beta1 (ExternalSecret), v1alpha1 (PushSecret) | ExternalSecret, PushSecret |
| cert-manager | github.com/cert-manager/cert-manager | v1 | Certificate |
Note: The PushSecret API is
v1alpha1(unstable). Its schema may change in future ESO releases. Pin the ESO module version ingo.modto 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
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:
| Name | Type | Description |
|---|---|---|
ctx | context.Context | Request context |
c | client.Client | Kubernetes API client |
scheme | *runtime.Scheme | Scheme for owner reference resolution |
owner | client.Object | Owning CR for garbage collection |
baseName | string | Base name for the ConfigMap (hash is appended as -<hash>) |
namespace | string | Namespace for the ConfigMap |
data | map[string]string | ConfigMap data entries |
Returns:
| Value | Description |
|---|---|
string | Actual ConfigMap name including the 8-character SHA256 hash suffix |
error | Non-nil on owner reference or API server failure |
Behavior:
- Computes a deterministic SHA256 hash from sorted
datakeys and values. - Truncates the hash to 8 hex characters and appends it as
baseName-<hash>. - Sets
Immutable: trueon the ConfigMap. - Sets a controller owner reference on the ConfigMap.
- If a ConfigMap with the same name already exists (
AlreadyExistserror), returns the name without error (idempotent). - Same data always produces the same hash (deterministic).
- Different data always produces a different hash.
Example:
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
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.Specwith the provided spec. - Readiness is determined by
IsDatabaseReadyon the existing resource.
IsDatabaseReady
func IsDatabaseReady(db *mariadbv1alpha1.Database) boolPure function. Returns true if the Database has a Ready condition with status True. Uses meta.IsStatusConditionTrue from k8s.io/apimachinery.
EnsureDatabaseUser
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
func IsUserReady(user *mariadbv1alpha1.User) boolPure function. Returns true if the User has a Ready condition with status True.
IsGrantReady
func IsGrantReady(grant *mariadbv1alpha1.Grant) boolPure function. Returns true if the Grant has a Ready condition with status True.
RunDBSyncJob
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
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.Specwith the provided spec. - Readiness is determined by
IsDeploymentReadyon the existing resource.
EnsureService
func EnsureService(
ctx context.Context,
c client.Client,
scheme *runtime.Scheme,
owner client.Object,
svc *corev1.Service,
) errorCreates 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
ClusterIPandClusterIPsvalues assigned by the API server before overwriting the spec. This prevents accidental ClusterIP reassignment, which would break existing DNS-based service discovery.
IsDeploymentReady
func IsDeploymentReady(deploy *appsv1.Deployment) boolPure function. Returns true when both conditions are met:
deploy.Status.ReadyReplicas >= *deploy.Spec.Replicas(defaults to 1 ifSpec.Replicasis nil).- The Deployment has an
Availablecondition with statusTrue.
Edge cases:
| Scenario | Result |
|---|---|
Spec.Replicas is nil | Defaults to 1 |
No Available condition | false |
ReadyReplicas < desired | false |
ReadyReplicas >= desired and Available=True | true |
Package: job
Manages Kubernetes Jobs and CronJobs for CobaltCore operators.
RunJob
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 viaIsJobComplete. Jobs are immutable after creation — they are not updated. - Reconcilers should call
RunJobon each reconciliation loop. The function is idempotent: calling it when the Job already exists and is complete returns(true, nil)without side effects.
EnsureCronJob
func EnsureCronJob(
ctx context.Context,
c client.Client,
scheme *runtime.Scheme,
owner client.Object,
cronJob *batchv1.CronJob,
) errorCreates or updates a CronJob with a controller owner reference.
Behavior:
- On create: sets controller owner reference, creates the resource.
- On update: overwrites
existing.Specwith the provided spec. CronJob spec updates take effect on the next scheduled run.
IsJobComplete
func IsJobComplete(job *batchv1.Job) boolPure function. Returns true if the Job has a Complete condition with status True.
Edge cases:
| Scenario | Result |
|---|---|
| No conditions | false |
Failed condition only | false |
Complete condition with status False | false |
Complete condition with status True | true |
IsJobFailed
func IsJobFailed(job *batchv1.Job) boolPure 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:
| Scenario | Result |
|---|---|
| No conditions | false |
Complete condition only | false |
Failed condition with status False | false |
Failed condition with status True | true |
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
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:
| Name | Type | Description |
|---|---|---|
ctx | context.Context | Request context |
c | client.Client | Kubernetes API client |
key | client.ObjectKey | Namespace and name of the ConfigMap |
Returns:
| Value | Description |
|---|---|
map[string]string | Parsed policy rules (action → rule expression) |
error | Non-nil when ConfigMap is missing, key is absent, or YAML is invalid |
Error conditions:
| Condition | Error |
|---|---|
| ConfigMap does not exist | Wrapped API server error (compatible with apierrors.IsNotFound) |
policy.yaml key absent | ConfigMap <key> does not contain key "policy.yaml" |
| Invalid YAML content | parsing policy.yaml from ConfigMap <key>: <parse error> |
Example:
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
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
ExternalSecretReadycondition type constant, not a raw string.
IsSecretReady
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
NotFounderror is treated as a normal condition ((false, nil)), not a failure. - When no
expectedKeysare provided, only checks for Secret existence. - When
expectedKeysare provided, returns(false, nil)if any key is absent fromSecret.Data.
GetSecretValue
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:
| Name | Type | Description |
|---|---|---|
key | client.ObjectKey | Namespace and name of the Secret |
dataKey | string | Key within Secret.Data to retrieve |
Returns: The decoded string value, or an error if the Secret or key is not found.
Error conditions:
| Condition | Error |
|---|---|
| Secret does not exist | Wrapped API server error |
Key not in Secret.Data | key "<dataKey>" not found in Secret <namespace>/<name> |
EnsurePushSecret
func EnsurePushSecret(
ctx context.Context,
c client.Client,
scheme *runtime.Scheme,
owner client.Object,
ps *esov1alpha1.PushSecret,
) errorCreates 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.Specwith the provided spec. - Uses the ESO
v1alpha1PushSecret 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
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.Specwith the provided spec. - Readiness is determined by
IsCertificateReadyon the existing resource. - cert-manager creates a Secret with the TLS certificate once the Certificate is ready. Use
GetTLSSecretto retrieve it.
IsCertificateReady
func IsCertificateReady(cert *certmanagerv1.Certificate) boolPure function. Returns true if the Certificate has a Ready condition (CertificateConditionReady) with status True (cmmeta.ConditionTrue).
GetTLSSecret
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.SecretincludingDatawithtls.crtandtls.keyentries (when created by cert-manager). - Returns a wrapped error on
NotFound— callers can check withapierrors.IsNotFound(err).
Cross-Package Dependencies
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:
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.LoadPolicyFromConfigMapEach 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.