Let TerdutServer customize its pod, and never manage its own ingress
spec.pod (api/v1alpha1/terdutserver_types.go): annotations, nodeSelector,
tolerations, affinity, topologySpreadConstraints, resources, pod and
container securityContext, serviceAccountName, extraEnv/extraEnvFrom,
extraVolumes/extraVolumeMounts, imagePullSecrets, and an optional
disruptionBudget. All direct corev1 passthrough -- no wrapper types buy
anything for any of these, matching how CloudNativePG and the Zalando
postgres-operator both expose the same knobs, and matching this repo's
own SweeperSpec precedent ("wrap only when a round-trip through a
different type buys something"). affinity is pure user-supplied
passthrough, not a toggle-plus-generated-default the way a multi-replica
cluster operator's pod anti-affinity usually is: this operator never
auto-generates one, since spec.replicas above 1 isn't a supported
topology (the sweeper/notifier singleton constraint). Considered and
declined for this round: priorityClassName, pod labels beyond
annotations, and a HorizontalPodAutoscaler -- the last of those would
directly contradict the singleton constraint above.
disruptionBudget is the one field here that isn't a plain PodTemplateSpec
knob: when set, the controller now reconciles a PodDisruptionBudget
selecting the TerdutServer's own pods (new terdutserver_pdb.go); clearing
it deletes any it previously created. New RBAC marker on
poddisruptionbudgets to match.
Driven by a public-release pass: looking past this project's own use case
at what a mature, general-purpose operator CRD exposes here (researched
against Zalando postgres-operator and CloudNativePG specifically), not
just the fields this install happened to need.
Separately, and found while answering a question about exposing
TerdutServer through Istio instead of Gateway API: spec.networking's own
doc comment quietly promised a Gateway API HTTPRoute this operator would
build eventually ("a near-term follow-up, not deferred"). That promise is
wrong for a public release -- an operator managing someone's ingress
mechanism for them is a worse default than not touching it at all, and a
surprise HTTPRoute appearing once that follow-up eventually landed would
have been exactly backwards for an Istio (or plain-Ingress, or
intentionally-unexposed) install. Made the non-goal explicit and
permanent instead (DESIGN.md §1), removed the dead `gatewayListener`
field it was the only consumer of (zero runtime call sites anywhere --
setting it already had no effect, so this is a schema cleanup, not a
behavior change), and corrected ROADMAP.md's framing. hostname/servicePort
stay: both are live (TERDUT_PUBLIC_URL, container/Service port), this
operator just never acts on hostname for exposure. Added
examples/networking (Gateway API HTTPRoute, Istio VirtualService) showing
how to expose the plain ClusterIP Service the operator already creates --
outside the operator itself, as illustrations, not as something
examples/demo applies automatically.
No new terdut-server version requirement: both changes are CRD/controller-
only, nothing about the API this operator's bootstrap flow depends on
changed.
This commit is contained in:
@@ -48,11 +48,26 @@ func (r *TerdutServerReconciler) reconcileDeployment(
|
||||
// overlapping during a rollout would both page for the same
|
||||
// incident (matches the chart's own deployment.yaml comment).
|
||||
deploy.Spec.Strategy = appsv1.DeploymentStrategy{Type: appsv1.RecreateDeploymentStrategyType}
|
||||
pod := srv.Spec.Pod
|
||||
deploy.Spec.Template = corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{Labels: labels},
|
||||
// pod.Annotations is assigned directly, not merged -- nothing
|
||||
// else sets pod-template annotations today. If a future change
|
||||
// needs the controller to set one of its own (e.g. a
|
||||
// Prometheus-scrape annotation), this needs to become a real
|
||||
// map merge with a stated precedence rather than silently
|
||||
// clobbering one side.
|
||||
ObjectMeta: metav1.ObjectMeta{Labels: labels, Annotations: pod.Annotations},
|
||||
Spec: corev1.PodSpec{
|
||||
EnableServiceLinks: new(false),
|
||||
InitContainers: []corev1.Container{waitForPostgresContainer(dbEnv)},
|
||||
EnableServiceLinks: new(false),
|
||||
NodeSelector: pod.NodeSelector,
|
||||
Tolerations: pod.Tolerations,
|
||||
Affinity: pod.Affinity,
|
||||
TopologySpreadConstraints: pod.TopologySpreadConstraints,
|
||||
SecurityContext: pod.SecurityContext,
|
||||
ServiceAccountName: pod.ServiceAccountName,
|
||||
ImagePullSecrets: pod.ImagePullSecrets,
|
||||
InitContainers: []corev1.Container{waitForPostgresContainer(dbEnv)},
|
||||
Volumes: pod.ExtraVolumes,
|
||||
Containers: []corev1.Container{{
|
||||
Name: "terdut-server",
|
||||
Image: fmt.Sprintf("%s:%s", srv.Spec.Image.Repository, srv.Spec.Image.Tag),
|
||||
@@ -61,9 +76,13 @@ func (r *TerdutServerReconciler) reconcileDeployment(
|
||||
ContainerPort: servicePort(srv),
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
}},
|
||||
Env: buildEnv(srv, dbEnv),
|
||||
LivenessProbe: healthzProbe(),
|
||||
ReadinessProbe: healthzProbe(),
|
||||
Env: buildEnv(srv, dbEnv),
|
||||
EnvFrom: pod.ExtraEnvFrom,
|
||||
VolumeMounts: pod.ExtraVolumeMounts,
|
||||
Resources: pod.Resources,
|
||||
SecurityContext: pod.ContainerSecurityContext,
|
||||
LivenessProbe: healthzProbe(),
|
||||
ReadinessProbe: healthzProbe(),
|
||||
}},
|
||||
},
|
||||
}
|
||||
@@ -151,7 +170,10 @@ func healthzProbe() *corev1.Probe {
|
||||
// field-for-field (confirmed against that source, not reconstructed from
|
||||
// DESIGN.md's illustrative YAML alone) — dbEnv (TERDUT_DB_DSN, optionally
|
||||
// PGPASSWORD) comes from resolveDatabaseEnv, since which of §8's two paths
|
||||
// produced it doesn't matter past this point.
|
||||
// produced it doesn't matter past this point. spec.pod.extraEnv is appended
|
||||
// last, after every fixed var -- this is the one place that owns "what env
|
||||
// this container gets," so the escape hatch lives here rather than being
|
||||
// appended separately in reconcileDeployment.
|
||||
func buildEnv(srv *terdutv1alpha1.TerdutServer, dbEnv []corev1.EnvVar) []corev1.EnvVar {
|
||||
env := []corev1.EnvVar{{Name: "TERDUT_ADDR", Value: fmt.Sprintf(":%d", servicePort(srv))}}
|
||||
env = append(env, dbEnv...)
|
||||
@@ -230,7 +252,7 @@ func buildEnv(srv *terdutv1alpha1.TerdutServer, dbEnv []corev1.EnvVar) []corev1.
|
||||
}
|
||||
}
|
||||
|
||||
return env
|
||||
return append(env, srv.Spec.Pod.ExtraEnv...)
|
||||
}
|
||||
|
||||
func secretEnvSource(ref *terdutv1alpha1.SecretKeyRef) *corev1.EnvVarSource {
|
||||
|
||||
Reference in New Issue
Block a user