INFINI Console Multi-Cluster Management Architecture: Handling Cross-Version Elasticsearch Clusters

INFINI Console implements a centralized control-plane architecture that abstracts heterogeneous Elasticsearch clusters into a unified management surface, automatically adapting to version-specific APIs across ES 1.x through 8.x, OpenSearch, and Easysearch distributions.

The infinilabs/console repository provides a production-grade solution for managing multiple Elasticsearch clusters from a single interface. This article examines how INFINI Console's multi-cluster management architecture enables simultaneous administration of diverse Elasticsearch distributions while maintaining strict security isolation and API compatibility.

Three-Layer Control-Plane Architecture

INFINI Console organizes its multi-cluster capabilities into three distinct logical layers that work together to provide a seamless management experience.

API Gateway Layer

The API Gateway exposes RESTful HTTP endpoints for cluster operations. In modules/elastic/api/init.go, the system registers all Elasticsearch-related routes, while core/elastic.go provides low-level helper methods for request processing. This layer enforces RBAC permissions before routing requests to the appropriate backend cluster.

Cluster Service Layer

The Cluster Service maintains cluster metadata in a dedicated system index (.infini_*). During first-run initialization in plugin/setup/setup.go, the system creates a hidden system cluster identified by GlobalSystemElasticsearchID = "infini_default_system_cluster". This service handles registration, health checks, and version discovery through the adapter.ClusterVersion function, which validates that connected clusters meet minimum version requirements (aborting with VersionTooOld for versions below 5.3).

Execution Engine Layer

The Execution Engine routes user requests to target clusters while adapting queries for version-specific APIs. Located in modules/elastic/api/v1/ and core/elastic.go, this layer builds per-cluster filters and selects appropriate request formats based on the discovered distribution and version.

Multi-Cluster Registration and Lifecycle

System Cluster Initialization

On first startup, plugin/setup/setup.go establishes the system cluster that stores Console's own metadata. This bootstrap process validates the Elasticsearch version and distribution before persisting configuration via the ORM layer (orm.Save).

User-Defined Cluster Registration

Administrators add production clusters through POST /elasticsearch/ endpoints. The handler validates endpoints, checks credentials, and stores metadata in the system index. Each cluster entry includes version and distribution information discovered at registration time.

Security Isolation

Access control operates through the GetClusterFilter function in core/elastic.go. This middleware extracts the user's allowed cluster IDs and injects an Elasticsearch-compatible terms filter:

return util.MapStr{
    "terms": util.MapStr{field: clusterIds},
}, false

This ensures users can only query clusters explicitly assigned to their role, with the filter applied at the Elasticsearch query level.

Cross-Version Elasticsearch Handling

INFINI Console does not lock users to a single Elasticsearch version. Instead, it discovers the target cluster's version and distribution at runtime, adapting requests accordingly.

Runtime Version Discovery

The adapter.ClusterVersion function in plugin/setup/setup.go queries the cluster's root endpoint to extract Version.Number and Version.Distribution. This discovery occurs during initial registration and periodic health checks, enabling dynamic adaptation to cluster upgrades.

Distribution-Specific Branching

Console handles three primary distributions: elastic.Elasticsearch, elastic.Easysearch, and elastic.Opensearch. The system branches logic based on ver.Distribution values. For example, index lifecycle management templates select version-specific DSL files such as template_ilm_1_12_1.tpl for Easysearch versions 1.12.1 and above.

API-Level Adaptation

Handlers in modules/elastic/api/v1/cluster_overview.go use the discovered major version to select appropriate document types (_doc versus doc) and index naming conventions. The initializeTemplate function in plugin/setup/setup.go injects version-specific variables into JSON payloads before transmission, ensuring compatibility across ES 1.x through 8.x APIs.

Code Examples

Registering a New Cluster via HTTP API

curl -X POST http://localhost:8080/elasticsearch/ \
  -H "Authorization: Bearer <jwt>" \
  -d '{
        "host": "es-node-01.example.com",
        "schema": "https",
        "username": "admin",
        "password": "s3cr3t",
        "tags": ["production","region-eu"]
      }'

Console stores this metadata in its system index and validates the cluster version using the same adapter.ClusterVersion logic found in plugin/setup/setup.go.

Retrieving Cluster Metadata in Go

import (
    "infini.sh/framework/core/elastic"
    "infini.sh/console/core"
)

// Get the client for a specific cluster ID
client, err := elastic.GetClient("my-cluster-id")
if err != nil {
    log.Fatal(err)
}

// Discover version/distribution
verInfo, _ := adapter.ClusterVersion(elastic.GetMetadata("my-cluster-id"))
fmt.Printf("Cluster runs %s %s\n", verInfo.Version.Distribution, verInfo.Version.Number)

This leverages the same version discovery mechanism used during initial system setup.

Executing Queries with Permission Isolation

func search(req *http.Request, clusterID string, query []byte) ([]byte, error) {
    // Build the per-user cluster filter
    filter, _ := core.GetClusterFilter(req, "metadata.labels.cluster_id")

    // Merge the filter into the query
    var body map[string]interface{}
    json.Unmarshal(query, &body)
    body["query"] = map[string]interface{}{
        "bool": map[string]interface{}{
            "filter": []interface{}{filter},
        },
    }
    payload, _ := json.Marshal(body)

    client, _ := elastic.GetClient(clusterID)
    return client.Search(payload)
}

The core.GetClusterFilter function injects the appropriate terms clause based on the user's allowed clusters, as implemented in core/elastic.go.

Key Implementation Files

The multi-cluster management system is implemented across the following source files:

Summary

  • INFINI Console employs a three-layer control-plane architecture (API Gateway, Cluster Service, Execution Engine) to abstract multiple Elasticsearch clusters into a unified management interface.

  • The system maintains cluster metadata in a dedicated system index (.infini_*), with a bootstrap system cluster (infini_default_system_cluster) created during first-run initialization in plugin/setup/setup.go.

  • Cross-version compatibility is achieved through runtime version discovery (adapter.ClusterVersion) and distribution-specific branching that adapts API calls for Elasticsearch 1.x-8.x, OpenSearch, and Easysearch.

  • Security isolation is enforced through GetClusterFilter in core/elastic.go, which injects user-specific terms filters into Elasticsearch queries to restrict access to authorized clusters only.

Frequently Asked Questions

How does INFINI Console discover the version of a newly registered Elasticsearch cluster?

During registration and periodic health checks, Console invokes adapter.ClusterVersion in plugin/setup/setup.go to query the target cluster's root endpoint. This function extracts Version.Number and Version.Distribution from the cluster metadata, validating that the version meets minimum requirements (5.3+) and identifying whether the cluster runs standard Elasticsearch, OpenSearch, or Easysearch.

Can INFINI Console manage clusters running different major versions of Elasticsearch simultaneously?

Yes. The Execution Engine in modules/elastic/api/v1/ and core/elastic.go maintains per-cluster version metadata and adapts API requests accordingly. For example, the system selects appropriate document types (_doc versus doc) and index naming conventions based on the target cluster's major version, allowing a single Console instance to manage ES 1.x through 8.x clusters concurrently.

How does the system prevent users from accessing unauthorized Elasticsearch clusters?

Console implements cluster-level isolation through the GetClusterFilter function in core/elastic.go. This middleware extracts the user's permitted cluster IDs from their role assignments and injects a terms filter into every Elasticsearch query, ensuring the search results only include data from authorized clusters. This filtering occurs at the Elasticsearch query level, providing robust security enforcement.

What happens during the initial setup of INFINI Console regarding cluster management?

During first-run initialization, plugin/setup/setup.go creates a hidden system cluster identified by GlobalSystemElasticsearchID = "infini_default_system_cluster". This system cluster stores Console's own metadata in dedicated system indices (.infini_*). The setup process validates the system cluster's version using adapter.ClusterVersion and initializes version-specific templates (such as template_ilm_1_12_1.tpl for Easysearch), establishing the foundation for subsequent multi-cluster management.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →