/* 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" "regexp" "strings" "codeberg.org/scip/esctl/pkg/cfg" ) var ( findIndex = regexp.MustCompile(`^\d{4}\.\d{2}\.\d{2}\-(.*)\-timeseries`) ) type Snapshot struct { Name string Forindex string Orphaned string Status string Start string IndexCount int } func SnapshotList(conf *cfg.Config) error { // get partial indicies ires, err := conf.DefaultCluster.ES.Cat.Indices().Do(context.Background()) if err != nil { return fmt.Errorf("Error getting indicies: %s", err) } indicies := map[string]int{} for _, index := range ires { name := strings.ReplaceAll(*index.Index, "partial-", "") indicies[name] = 1 } // get snapshots sres, err := conf.DefaultCluster.ES.Cat.Snapshots().Do(context.Background()) if err != nil { return fmt.Errorf("Error getting snapshots: %s", err) } slog.Debug("ES result", "indicies", sres) snapshots := []*Snapshot{} // original snapshot names for _, snapshot := range sres { snap := &Snapshot{ Name: *snapshot.Id, Status: *snapshot.Status, Start: fmt.Sprintf("%s", snapshot.StartTime), Forindex: indexFromSnapshot(*snapshot.Id), Orphaned: "no", } _, exists := indicies[snap.Forindex] if !exists { snap.Orphaned = "orphaned" } if (conf.Failed && snap.Orphaned == "orphaned") || !conf.Failed { snapshots = append(snapshots, snap) } } table := NewTable(5, len(snapshots)) table.Addheaders("name", "index", "start", "orphaned", "status") for idx, snap := range snapshots { table.entries[idx] = []string{ snap.Name, snap.Forindex, snap.Start, snap.Orphaned, snap.Status, } } table.Sort() table.PrintMarkdown() return nil } func SnapshotShow(conf *cfg.Config, snapshot string) error { res, err := conf.DefaultCluster.ES.Snapshot.Get("*", snapshot).Do(context.Background()) if err != nil { return fmt.Errorf("failed to get snapshot: %s", err) } slog.Debug("ES result", "snapshot", res) if res.Total == 0 { return errors.New("no snapshot retrieved") } table := NewTable(2, 17) table.Addheaders("field", "value") snap := res.Snapshots[0] table.entries = [][]string{ {"snapshot", snapshot}, {"uuid", snap.Uuid}, {"repository", *snap.Repository}, {"version_id", fmt.Sprintf("%d", snap.VersionId)}, {"version", *snap.Version}, {"include_global_state", fmt.Sprintf("%t", *snap.IncludeGlobalState)}, {"state", *snap.State}, {"start_time", fmt.Sprintf("%s", snap.StartTime)}, {"end_time", fmt.Sprintf("%s", snap.EndTime)}, {"duration_in_millis", fmt.Sprintf("%d", *snap.DurationInMillis)}, {"shards-total", fmt.Sprintf("%d", snap.Shards.Total)}, {"shards-failed", fmt.Sprintf("%d", snap.Shards.Failed)}, {"shards-successful", fmt.Sprintf("%d", snap.Shards.Successful)}, } table.PrintMarkdown() return nil } func indexFromSnapshot(snapshot string) string { matches := findIndex.FindStringSubmatch(snapshot) if len(matches) != 2 { return "" } return matches[1] }