/* 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" "errors" "fmt" "log/slog" "codeberg.org/scip/esctl/pkg/cfg" "codeberg.org/scip/esctl/pkg/printer" "github.com/elastic/go-elasticsearch/v9/typedapi/types" ) const ( shard_pause = `cluster.routing.allocation.enable` ) func CcrShardPause(conf *cfg.Config) error { return ClusterSettingsSetSingle(conf, shard_pause, "none") } func CcrShardResume(conf *cfg.Config) error { return ClusterSettingsSetSingle(conf, shard_pause, "all") } func CcrStatus(conf *cfg.Config, leader, follower string) error { if !checkClusterIsLeader(conf, leader) { if !checkClusterIsLeader(conf, follower) { return errors.New("leader/follower attribution is invalid, both clusters are followers") } // reverse attribution f := follower follower = leader leader = f slog.Debug("leader/follower attribution is invalid, reversing", "leader", leader, "follower", follower) } indices := ClusterIndices{} for _, alias := range []string{leader, follower} { res, err := conf.Clusters[alias].ES().Cat.Indices(). Do(context.Background()) if err != nil { return fmt.Errorf("failed to get indicies on %s: %s", alias, esErrorString(err)) } indices[alias] = make(map[string]*types.IndicesRecord, len(res)) for _, index := range res { indices[alias][*index.Index] = &index } } if !checkClusterStatus(conf, leader, follower) { return errors.New("one of the two clusters is in a failed state") } findIlmErrors(conf, leader, follower) if findIndicesOnlyOnLeader(conf, indices, leader, follower) && findOrphanedIndices(conf, indices, leader, follower) && findFailedFollowerIndices(conf, indices, follower) { fmt.Println("everything's hunky-dory.") } return nil } func CcrRemoteInfo(conf *cfg.Config, index string) error { res, err := conf.DefaultCluster.ES().Cluster.RemoteInfo(). Do(context.Background()) if err != nil { return fmt.Errorf("failed to retrieve follower info: %s", esErrorString(err)) } slog.Debug("ccr remote info", "info", res) remote := "" var info *types.ClusterRemoteProxyInfo for name, data := range res { remote = name info = data.(*types.ClusterRemoteProxyInfo) break } if remote == "" { return errors.New("cluster doesn't follow any other") } mode := "follower" if checkClusterIsLeader(conf, conf.CurrentCluster) { mode = "leader" } table := printer.NewTable(conf, 2, 5) table.Addheaders("ccr remote property", "value") table.Entries = [][]string{ {"Remote Cluster", remote}, {"CCR Mode", mode}, {"Connected", fmt.Sprintf("%t", info.Connected)}, {"Num Proxy Sockets Connected", fmt.Sprintf("%d", info.NumProxySocketsConnected)}, {"Proxy Address", info.ProxyAddress}, } if err := table.Print(); err != nil { return err } return nil }