/* Copyright © 2026 Thomas von Dein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package es import ( "context" "fmt" "regexp" "codeberg.org/scip/esctl/pkg/cfg" "github.com/elastic/go-elasticsearch/v9/typedapi/types" ) const ( DefaultExclude = `(part|monitoring|.internal|metrics-endpoint)` ) type ClusterIndices map[string]map[string]*types.IndicesRecord func ClusterCompare(conf *cfg.Config, leader, follower string) error { indices := ClusterIndices{} for _, alias := range []string{leader, follower} { cat := conf.Clusters[alias].ES.Cat.Indices(). // we need to add custom request headers, required for older ES instances Header("content-type", "application/json"). Header("accept", "application/json") res, err := cat.Do(context.Background()) if err != nil { return fmt.Errorf("Error getting indicies on %s: %s", alias, err) } indices[alias] = make(map[string]*types.IndicesRecord, len(res)) for _, index := range res { indices[alias][*index.Index] = &index } } findIndicesOnlyOnMaster(conf, indices, leader, follower) return nil } // look for indicies only on leader func findIndicesOnlyOnMaster(conf *cfg.Config, indices ClusterIndices, leader, follower string) { exclude := regexp.MustCompile(DefaultExclude) if conf.Exclude != "" { exclude = regexp.MustCompile(conf.Exclude) } indexOnlyOnLeader := map[string]*types.IndicesRecord{} for name, index := range indices[leader] { if exclude.MatchString(name) { continue } _, followerHasIt := indices[follower][name] if !followerHasIt { // fetch index details res, err := conf.Clusters[leader].ES.Indices.Get(name). Header("content-type", "application/json"). Header("accept", "application/json"). Do(context.Background()) if err != nil { continue // ignore it then } _, defined := res[name] if !defined { // json response map didn't contain the index continue } isWritable := false for _, alias := range res[name].Aliases { if *alias.IsWriteIndex { isWritable = true break } } if isWritable { // ignore index if associated alias index is writing continue } indexOnlyOnLeader[name] = index } } idx := 0 table := NewTable(3, len(indexOnlyOnLeader)) table.Addheaders("index only on leader", "size", "docscount") for name, index := range indexOnlyOnLeader { name := Colorize("red", name) table.entries[idx] = []string{name, *index.DatasetSize, *index.DocsCount} idx++ } table.Sort() table.PrintMarkdown() }