/* 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" "codeberg.org/scip/esctl/pkg/printer" ) 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("failed to get indicies: %s", esErrorString(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("failed to get snapshots: %s", esErrorString(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 := printer.NewTable(conf, 5, len(snapshots)) table.Addheaders("name", "index", "start", "orphaned", "status") for idx, snap := range snapshots { table.Entries[idx] = []any{ snap.Name, snap.Forindex, snap.Start, snap.Orphaned, snap.Status, } } table.Sort() if err := table.Print(); err != nil { return err } 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", esErrorString(err)) } slog.Debug("ES result", "snapshot", res) if res.Total == 0 { return errors.New("no snapshot retrieved") } table := printer.NewTable(conf, 2, 17) table.Addheaders("snapshot property", "value") snap := res.Snapshots[0] table.Entries = [][]any{ {"snapshot", snapshot}, {"uuid", snap.Uuid}, {"repository", *snap.Repository}, {"version_id", snap.VersionId}, {"version", *snap.Version}, {"include_global_state", *snap.IncludeGlobalState}, {"state", *snap.State}, {"start_time", snap.StartTime}, {"end_time", snap.EndTime}, {"duration_in_millis", *snap.DurationInMillis}, {"shards-total", snap.Shards.Total}, {"shards-failed", snap.Shards.Failed}, {"shards-successful", snap.Shards.Successful}, } if err := table.Print(); err != nil { return err } return nil } func indexFromSnapshot(snapshot string) string { matches := findIndex.FindStringSubmatch(snapshot) if len(matches) != 2 { return "" } return matches[1] }