Istio — Interfaces#

Sampling note#

Istio has 263 interface definitions outside vendor/. The 8 interfaces analyzed here were selected because they appear directly in the architecture result as the load-bearing abstractions of the control plane. Trivial single-method helpers, test-only interfaces, and widget-level watcher interfaces were skipped.


Interface catalog#

ConfigStore#

  • Package: pilot/pkg/model
  • File: pilot/pkg/model/config.go:125
  • Methods:
    Schemas() collection.Schemas
    Get(typ config.GroupVersionKind, name, namespace string) *config.Config
    List(typ config.GroupVersionKind, namespace string) []config.Config
    Create(config config.Config) (revision string, err error)
    Update(config config.Config) (newRevision string, err error)
    UpdateStatus(config config.Config) (newRevision string, err error)
    Delete(typ config.GroupVersionKind, name, namespace string, resourceVersion *string) error
  • Purpose: Platform-agnostic CRUD access to all Istio configuration resources (VirtualService, DestinationRule, Gateway, AuthorizationPolicy, PeerAuthentication, Sidecar, Telemetry, etc.). The interface is intentionally storage-agnostic: it makes no assumptions about Kubernetes, etcd, or any backend.
  • Implementations:
    • crdclient.Client (pilot/pkg/config/kube/crdclient/client.go) — Kubernetes CRD-backed store using typed kclient informers
    • file.KubeSource (pilot/pkg/config/file/store.go) — filesystem-backed store (used in integration tests and offline mode)
    • configaggregate.Store — fan-out aggregate that reads from multiple backing stores and merges results
    • FakeStore — test double
  • Design quality: Well-segregated CRUD contract. Only 7 methods; each is orthogonal. The GroupVersionKind key is the right abstraction for a multi-schema config system. The documentation notes intentional eventual consistency (mutations may not be immediately visible on Get), which is honest about k8s informer semantics.

ConfigStoreController#

  • Package: pilot/pkg/model
  • File: pilot/pkg/model/config.go:172
  • Methods:
    // embeds ConfigStore, plus:
    RegisterEventHandler(kind config.GroupVersionKind, handler EventHandler)
    Run(stop <-chan struct{})
    HasSynced() bool
    KrtCollection(kind config.GroupVersionKind) krt.Collection[config.Config]
  • Purpose: Extends ConfigStore with lifecycle management and change notifications. Represents a running local cache (backed by an informer) that propagates config-change events to downstream handlers. The KrtCollection method is a newer addition that exposes the underlying krt collection, enabling reactive pipelines to subscribe directly without the legacy callback mechanism.
  • Implementations: Same as ConfigStore (every production implementation satisfies both).
  • Design quality: Appropriate ISP split — read-only callers (xDS generators) receive only ConfigStore; lifecycle owners receive the full controller interface. The KrtCollection escape hatch is a transitional affordance while migrating from event callbacks to krt-style reactive composition.

ServiceDiscovery#

  • Package: pilot/pkg/model
  • File: pilot/pkg/model/service.go:929
  • Methods:
    // embeds NetworkGatewaysWatcher and AmbientIndexes, plus:
    Services() []*Service
    GetService(hostname host.Name) *Service
    GetProxyServiceTargets(proxy *Proxy) []ServiceTarget
    GetProxyWorkloadLabels(proxy *Proxy) labels.Instance
    MCSServices() []MCSServiceInfo
  • Purpose: Unified abstraction for enumerating services, endpoints, and workload metadata across one or more service registries. Istio can simultaneously watch Kubernetes services, ServiceEntry CRDs, and cloud-provider registries; ServiceDiscovery hides this multiplicity from the xDS generation layer.
  • Implementations:
    • aggregate.Controller (pilot/pkg/serviceregistry/aggregate) — fans out to N registries and merges
    • serviceregistry.Instance (pilot/pkg/serviceregistry/instance.go) — combines model.Controller + model.ServiceDiscovery into a per-registry handle; Kubernetes and ServiceEntry controllers both implement this interface
  • Design quality: The interface is relatively broad (5 own methods + embedded interfaces), but each method is genuinely needed by the xDS generators that consume it. The embedding of AmbientIndexes deserves scrutiny — it bundles sidecar-mode and ambient-mode concerns in a single type, which creates coupling (any ServiceDiscovery implementation must stub out ambient methods even if they’re irrelevant). This is a pragmatic trade-off to avoid a separate runtime type assertion path for ambient mode.

AmbientIndexes#

  • Package: pilot/pkg/model
  • File: pilot/pkg/model/service.go:965
  • Methods:
    ServicesWithWaypoint(key string) []ServiceWaypointInfo
    AddressInformation(addresses sets.String) ([]AddressInfo, sets.String)
    AdditionalPodSubscriptions(proxy *Proxy, allAddresses, currentSubs sets.String) sets.String
    Policies(requested sets.Set[ConfigKey]) []WorkloadAuthorization
    ServicesForWaypoint(WaypointKey) []ServiceInfo
    WorkloadsForWaypoint(WaypointKey) []WorkloadInfo
    ServiceInfo(key string) *ServiceInfo
  • Purpose: Ambient mesh queries — lookups needed by the PCDS and workload xDS generators to serve ztunnel and waypoint proxies. The ambient data model differs fundamentally from the sidecar model: identity is IP/address-based, not pod-centric, and waypoints act as L7 proxies for services. These 7 methods encapsulate that alternative index.
  • Implementations: ambientindex.AmbientIndexesImpl (built using krt collections); stub implementations in test code.
  • Design quality: Reasonably cohesive as an ambient-specific contract. The choice to embed it directly in ServiceDiscovery rather than compose it via an interface field means non-ambient implementations must provide no-op stubs for all 7 methods, which is slightly leaky.

XDSUpdater#

  • Package: pilot/pkg/model
  • File: pilot/pkg/model/push_context.go:327
  • Methods:
    EDSUpdate(shard ShardKey, hostname, namespace string, entry []*IstioEndpoint)
    EDSCacheUpdate(shard ShardKey, hostname, namespace string, entry []*IstioEndpoint)
    SvcUpdate(shard ShardKey, hostname, namespace string, event Event)
    ConfigUpdate(req *PushRequest)
    ProxyUpdate(clusterID cluster.ID, ip string)
    RemoveShard(shardKey ShardKey)
  • Purpose: The callback bridge between service registries (Kubernetes controllers, ServiceEntry controller) and the xDS push pipeline (DiscoveryServer). The interface inverts the dependency so that registries call upward into the discovery server without a direct import cycle. EDSUpdate triggers a push; EDSCacheUpdate only updates state (for bulk loading before serving). ConfigUpdate is the full-push trigger. ProxyUpdate allows per-proxy targeted pushes.
  • Implementations:
    • xds.DiscoveryServer — the primary implementation; ConfigUpdate enqueues to pushChannel, EDSUpdate enqueues an incremental push
    • xdsfake.Updater (pilot/pkg/serviceregistry/util/xdsfake/updater.go) — test double that records calls
    • FakeEndpointIndexUpdater — minimal test double
  • Design quality: Clean dependency-inversion interface. The split between EDSUpdate (push) and EDSCacheUpdate (no-push) is a subtle but important optimization for the case where multiple endpoint shards are being loaded; callers batch with cache-only updates, then trigger a single push. 6 methods is slightly wide but each is distinct in semantics.

XdsResourceGenerator / XdsDeltaResourceGenerator#

  • Package: pilot/pkg/model
  • File: pilot/pkg/model/context.go:290,296
  • Methods:
    // XdsResourceGenerator:
    Generate(proxy *Proxy, w *WatchedResource, req *PushRequest) (Resources, XdsLogDetails, error)
    
    // XdsDeltaResourceGenerator (embeds XdsResourceGenerator):
    GenerateDeltas(proxy *Proxy, req *PushRequest, w *WatchedResource) (Resources, DeletedResources, XdsLogDetails, bool, error)
  • Purpose: The plugin interface for xDS resource generation. DiscoveryServer keeps a map[string]XdsResourceGenerator keyed by xDS type URL (e.g., "type.googleapis.com/envoy.config.listener.v3.Listener"). When a push is needed for a proxy, the server looks up the generator for each subscribed type and calls Generate. The delta variant is for Incremental xDS (Delta ADS) where only changed/removed resources need to be sent.
  • Implementations:
    • LdsGenerator — Listener Discovery Service (LDS)
    • CdsGenerator (implements XdsDeltaResourceGenerator) — Cluster Discovery Service (CDS)
    • EdsGenerator (implements XdsDeltaResourceGenerator) — Endpoint Discovery Service (EDS)
    • RdsGenerator — Route Discovery Service (RDS)
    • NdsGenerator — Name Discovery Service (NDS, DNS)
    • EcdsGenerator — Extension Config Discovery Service (ECDS)
    • PcdsGenerator — Policy Config Discovery Service (PCDS, ambient RBAC)
    • SecretGen — Secret Discovery Service (SDS, TLS certificates)
    • Ambient workload generator (registered for ambient mode type URL)
  • Design quality: Near-perfect ISP. The single-method XdsResourceGenerator interface is one of the cleanest in the codebase — trivially mockable, trivially composable, with zero coupling between generators. The delta sub-interface is optional opt-in, which is the right choice. This design is the direct reason why adding a new xDS type (PCDS, workload) requires zero changes to the core push loop.

ConfigGenerator#

  • Package: pilot/pkg/networking/core
  • File: pilot/pkg/networking/core/configgen.go:28
  • Methods:
    BuildListeners(node *model.Proxy, push *model.PushContext) []*listener.Listener
    BuildClusters(node *model.Proxy, req *model.PushRequest) ([]*discovery.Resource, model.XdsLogDetails)
    BuildDeltaClusters(proxy *model.Proxy, updates *model.PushRequest, watched *model.WatchedResource) ([]*discovery.Resource, []string, model.XdsLogDetails, bool)
    BuildHTTPRoutes(node *model.Proxy, req *model.PushRequest, routeNames []string) ([]*discovery.Resource, model.XdsLogDetails)
    BuildNameTable(node *model.Proxy, push *model.PushContext) *dnsProto.NameTable
    BuildExtensionConfiguration(node *model.Proxy, push *model.PushContext, extensionConfigNames []string, pullSecrets map[string][]byte) []*core.TypedExtensionConfig
    MeshConfigChanged(mesh *meshconfig.MeshConfig)
  • Purpose: A higher-level, Envoy-aware translation interface that sits above XdsResourceGenerator. Where XdsResourceGenerator.Generate is protocol-generic, ConfigGenerator methods work directly with Envoy proto types (listener, cluster, route). The individual xDS generators (LdsGenerator, CdsGenerator, etc.) delegate to ConfigGenerator methods on their internal ConfigGeneratorImpl reference. ConfigGenerator is thus the domain-logic layer; the xDS generators handle framing, caching, and protocol handling.
  • Implementations: ConfigGeneratorImpl — the sole production implementation; no test doubles needed because the xDS generators test via XdsResourceGenerator.
  • Design quality: 7 methods makes it a moderately wide interface. Only one implementation exists, so the interface primarily serves as a seam for testing and to document the expected contract. MeshConfigChanged is a cache-invalidation hook and feels slightly out of place, but it is legitimately needed since ConfigGeneratorImpl holds the accessLogBuilder cache.

XdsCache#

  • Package: pilot/pkg/model
  • File: pilot/pkg/model/xds_cache.go:36
  • Methods:
    Run(stop <-chan struct{})
    Add(entry XdsCacheEntry, pushRequest *PushRequest, value *discovery.Resource)
    Get(entry XdsCacheEntry) *discovery.Resource
    Clear(sets.Set[ConfigKey])
    ClearAll()
    Keys(t string) []any       // debug/test only
    Snapshot() []*discovery.Resource  // debug/test only
  • Purpose: Content-addressed cache for encoded Envoy proto.Any xDS resources. Keys are XdsCacheEntry objects whose DependentConfigs() returns the set of config hashes this resource depends on; when any of those configs change, the cache entry is invalidated. This avoids re-encoding identical protobuf between push cycles. The Add method silently drops writes for stale PushRequests, preventing a race where a slow generator writes stale data after a newer push cycle has already started.
  • Implementations: XdsCacheImpl — the sole production implementation (backed by four type-specific typedXdsCache[K] instances for CDS, EDS, RDS, SDS).
  • Design quality: The design of a separate XdsCacheEntry interface is elegant — it decouples the cache from knowledge of specific resource types and lets each generator define its own cache key logic. Keys/Snapshot being explicitly documented as “testing/debug only” in comments is a good signal; they should arguably be in a separate interface.

Collection[T]#

  • Package: pkg/kube/krt
  • File: pkg/kube/krt/core.go:28
  • Methods:
    GetKey(k string) *T
    List() []T
    EventStream[T]          // Register, RegisterBatch
    Metadata() Metadata
    where EventStream[T] embeds Syncer and adds:
    Register(f func(o Event[T])) HandlerRegistration
    RegisterBatch(f func(o []Event[T]), runExistingState bool) HandlerRegistration
  • Purpose: The foundational abstraction for Istio’s declarative reactive controller framework (krt). A Collection[T] is a live, typed view of a Kubernetes resource or a derived transformation. Consumers list the current state or subscribe to change events; the krt runtime propagates changes through the dependency graph automatically. This eliminates the need to write imperative reconcile-loops with manual mutex management.
  • Implementations: Multiple internal implementations — StaticCollection, informerCollection[T] (wrapping a kclient informer), derivedCollection[T] (built via NewCollection / NewManyCollection transformations), Singleton[T].
  • Design quality: The generic interface is well-designed: small surface, clear semantics, and the EventStream embedding follows Go’s composability idiom. The internalCollection super-interface (unexported) adds uid/dump/augment methods needed by the krt engine without exposing them to consumers — correct information hiding. The framework is a genuine architectural investment for the next generation of Istio controllers.

Interface patterns#

  • Size distribution: The core interfaces average 5–7 methods. The single-method XdsResourceGenerator is a standout example of ISP applied perfectly. The broader interfaces (ServiceDiscovery, ConfigGenerator) reflect real domain complexity rather than design laziness — each method is genuinely used by multiple callers.

  • Embedding: Used consistently for interface composition:

    • ConfigStoreController embeds ConfigStore
    • ServiceDiscovery embeds NetworkGatewaysWatcher and AmbientIndexes
    • XdsDeltaResourceGenerator embeds XdsResourceGenerator
    • serviceregistry.Instance embeds model.Controller and model.ServiceDiscovery
    • EventStream[T] embeds Syncer This produces clean ISP splits: callers that only need read access get ConfigStore; callers needing lifecycle management get ConfigStoreController.
  • Implicit satisfaction: All interfaces are defined in consumer-side packages (pilot/pkg/model, pkg/kube/krt) rather than provider-side packages. Implementations use var _ Interface = (*ConcreteType)(nil) compile-time assertions throughout. This is idiomatic Go and correctly places the contract ownership with the consumer.

  • stdlib interfaces used: Minimal direct use. XdsCacheEntry uses a pattern resembling hash.Hash. The Syncer type (embedded in EventStream) is an Istio-internal interface analogous to cache.InformerSynced. Most interfaces are domain-specific rather than wrapping stdlib contracts.


Key abstractions#

  1. XdsResourceGenerator — The single most elegant interface in the codebase. One method, zero coupling, directly enables the plugin-map architecture of the xDS push loop. Adding a new xDS resource type is a one-file change: implement Generate, register in the map. This is the pattern that allowed PCDS and workload xDS for Ambient mode to be added without touching the push loop.

  2. ConfigStore / ConfigStoreController — The gateway to all Istio user configuration. By abstracting over Kubernetes CRDs, filesystem, and xDS-over-MCP backends, it makes the entire xDS generation layer portable and testable. The ISP split into a read-only ConfigStore and a lifecycle-owning ConfigStoreController is the right design decision.

  3. XDSUpdater — The dependency-inversion interface that decouples service registries from DiscoveryServer. Without it, pilot/pkg/serviceregistry/kube/controller would need to import pilot/pkg/xds, creating a circular dependency. The separation of EDSUpdate (push-triggering) from EDSCacheUpdate (cache-only) shows mature operational thinking about bulk loading.

  4. ServiceDiscovery — The unified registry abstraction. Its embedding of AmbientIndexes is architecturally significant: it forces all registry implementations to speak both the sidecar and ambient vocabularies, which keeps the DiscoveryServer push path generic. The trade-off is that non-ambient registries must implement ambient stubs.

  5. Collection[T] — The foundational type of the krt framework. It is the most architecturally forward-looking interface in the codebase, representing Istio’s intent to replace all imperative Kubernetes controller code with declarative reactive pipelines. Its design — generic, event-streaming, with lazy propagation — is a blueprint for how large-scale Kubernetes controller frameworks should be structured.


Interface-driven extensibility#

Istio achieves extensibility at three levels through interfaces:

  1. xDS type extensibility via XdsResourceGenerator: New Envoy resource types (PCDS for policy, workload for ambient ztunnel) are registered into DiscoveryServer’s generator map with no changes to the core push loop. Third-party Istio forks have used this to add custom xDS types.

  2. Backend extensibility via ConfigStore / ServiceDiscovery: In principle, non-Kubernetes backends (cloud registries, custom service meshes) can plug in by implementing these interfaces. In practice, the aggregate controller pattern (configaggregate, serviceregistry/aggregate) means new backends are added as additional registries that are merged transparently.

  3. Controller modernization via Collection[T]: The krt framework provides an extension point for building new control loops declaratively. New subsystems (Ambient mode’s workload indexing, gateway-api integration) are being built as krt collections composed from existing collections, enabling safe incremental replacement of legacy controller code.