Documentation
¶
Overview ¶
Package meshconfig hosts the MeshConfig CRD machinery that runs in the aether-controller: a validating admission webhook (protovalidate) and a reconciler that projects the singleton MeshConfig custom resource into the ConfigMap the agent consumes. It works against the typed MeshConfig object (api/aether/config/v1), not unstructured. See docs/proposals/015_mesh-config.md.
Index ¶
Constants ¶
const ( // DefaultCABundleCallTimeout bounds the read and the write of ONE webhook // configuration. Both are single-object calls an apiserver answers in // milliseconds, so a call that is still out after this long is failing, and // is reported and retried as a failure (issue #1431). DefaultCABundleCallTimeout = 15 * time.Second // DefaultCABundleRetryMin and DefaultCABundleRetryMax bound the backoff // between retries while a webhook configuration cannot be injected. The // ceiling is also how long the injector takes, at worst, to notice that a // missing permission has been granted. DefaultCABundleRetryMin = time.Second DefaultCABundleRetryMax = time.Minute // CABundleInjectionFailuresMetric counts failed injection attempts, one per // webhook configuration and attempt, by `kind` (validating, mutating) and // `reason` (trust_bundle, get, update). Expected to stay 0. CABundleInjectionFailuresMetric = "aether.controller.webhook.cabundle_injection_failures" )
const ( // SingletonName is the only MeshConfig name the controller acts on; the CRD // is a cluster-scoped singleton. SingletonName = "default" // ConfigMapKey is the key under which the projected config is written. ConfigMapKey = "mesh-config.yaml" // DefaultMeshConfigMapName is the ConfigMap the reconciler projects into and // that the agent mounts. DefaultMeshConfigMapName = "aether-mesh-config" )
Variables ¶
This section is empty.
Functions ¶
func RenderConfigMapData ¶
func RenderConfigMapData(spec *configv1.MeshConfigSpec) (map[string]string, error)
RenderConfigMapData serializes a MeshConfig spec to the YAML document the agent loads, returning the ConfigMap `data` map. A nil spec renders an empty document (the agent then inherits everything from the aether config).
func Validate ¶
func Validate(spec *configv1.MeshConfigSpec) error
Validate runs protovalidate on a MeshConfig spec. A nil spec (the proxy inherits everything from the aether config) is valid.
Types ¶
type CABundleInjector ¶
type CABundleInjector struct {
// Reader reads the webhook configurations. It MUST be uncached (the
// manager's GetAPIReader): see the type comment. Two single-object reads per
// pass cost nothing next to a cluster-wide informer on both resources, and
// need only the `get` permission.
Reader client.Reader
// Writer updates them.
Writer client.Writer
// Source is this workload's SVID source. It is the narrow SVIDSource interface
// rather than a *spire.Source because the controller's source may still be
// waiting for its first SVID when this runnable starts (issue #740): the
// initial injection is then deferred (INFO, not ERROR) and performed on the
// Updated() wake that WaitingSource fires when the SVID lands.
Source spire.SVIDSource
WebhookConfigName string
// MutatingWebhookConfigName is the pod-mutating MutatingWebhookConfiguration
// to keep in sync too; empty skips it.
MutatingWebhookConfigName string
Log *slog.Logger
// CallTimeout, RetryMin and RetryMax override the Default* constants above;
// zero keeps the default.
CallTimeout time.Duration
RetryMin time.Duration
RetryMax time.Duration
// contains filtered or unexported fields
}
CABundleInjector keeps the webhooks' caBundle in sync with the SPIRE trust bundle. The webhooks are served with a SPIRE X.509 SVID, so the kube-apiserver must trust the SPIRE CA — this runnable writes the current bundle into the ValidatingWebhookConfiguration (and the MutatingWebhookConfiguration, when one is named) on startup and on every SVID rotation. It runs on the leader only (a cluster-wide single writer).
Two properties are load-bearing (issue #1431):
- It cannot hang. Every read goes to the apiserver through Reader, never through the manager's informer cache, and each configuration's read and write share one bounded context. A cached read waits, with no deadline of its own, for an informer that may never sync — a missing list permission is enough — and the loop that should have reported the failure never came back.
- The configurations are independent. Each is read, written, reported and retried on its own, so one that keeps failing never stops the other from following a trust-bundle rotation.
func NewCABundleInjector ¶
func NewCABundleInjector(m ClientSource, source spire.SVIDSource, webhookConfigName, mutatingWebhookConfigName string, log *slog.Logger) *CABundleInjector
NewCABundleInjector builds the injector from a manager's clients: reads go through the manager's API reader, writes through its client.
func (*CABundleInjector) NeedLeaderElection ¶
func (i *CABundleInjector) NeedLeaderElection() bool
NeedLeaderElection keeps caBundle writes to a single replica.
func (*CABundleInjector) Start ¶
func (i *CABundleInjector) Start(ctx context.Context) error
Start injects the bundle once, then re-injects whenever the SPIRE source reports a rotation, until the context is cancelled. A pass in which a configuration failed is repeated with exponential backoff, so a failure never has to wait for the next rotation to be retried.
It never fails startup: failurePolicy=Ignore means an un-injected webhook fails open, and the retry keeps trying.
type ClientSource ¶
type ClientSource interface {
// GetClient is the manager's client: reads come from the informer cache.
GetClient() client.Client
// GetAPIReader reads from the apiserver directly.
GetAPIReader() client.Reader
}
ClientSource is the part of a controller-runtime manager the injector takes its clients from. The manager satisfies it.
type Reconciler ¶
type Reconciler struct {
client.Client
ConfigMapName string
FallbackNamespace string
Log *slog.Logger
}
Reconciler projects each namespace's MeshConfig CR into a ConfigMap in that SAME namespace (co-located), which the agent/edge in that namespace mount. MeshConfig is namespaced: a namespace's `default` CR overrides the FallbackNamespace (aether-system) MeshConfig field-by-field, so a namespace inherits the mesh-wide config unless it sets its own. It re-validates with protovalidate before writing, so an invalid CR that slipped past the (best-effort) webhook never overwrites the last-good ConfigMap — the failure surfaces on the CR's status instead.
func (*Reconciler) Reconcile ¶
func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error)
Reconcile validates a namespace's effective MeshConfig and upserts the co-located ConfigMap. A validation failure is recorded on the CR status and does not return an error (retrying wouldn't help an invalid spec).
func (*Reconciler) SetupWithManager ¶
func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error
SetupWithManager registers the reconciler. A change to a namespace's MeshConfig re-projects that namespace (For); a change to the FallbackNamespace MeshConfig re-projects every inheriting namespace (the fan-out map func).
type Validator ¶
Validator is an admission webhook that rejects MeshConfig resources whose spec fails protovalidate — the same check the agent's file loader and the reconciler run, so a CR can never describe a config the binaries would refuse to load. It is served by the controller's shared /validate dispatcher (see controller/internal/webhook), keyed by the MeshConfig Kind.