mirror of
https://codeberg.org/scip/esctl.git
synced 2026-08-24 06:24:18 +02:00
add index template {ls,sh} (#33)
This commit is contained in:
@@ -48,19 +48,31 @@ type Cluster struct {
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
ConfigFile string // -c
|
||||
CurrentCluster string // -C
|
||||
Debug bool // -d
|
||||
Output string // -o <mode>
|
||||
Clusters map[string]*Cluster
|
||||
DefaultCluster *Cluster
|
||||
Index string // index: -i
|
||||
Failed, Partials bool // index: flags
|
||||
Shards, Replicas int // index create+allocation: -s -r
|
||||
Wait bool // index create: -w
|
||||
Primary bool // index allocation: -p
|
||||
Searchable bool // index fields: -s
|
||||
Aggretable bool // index fields: -a
|
||||
ConfigFile string // -c
|
||||
CurrentCluster string // -C
|
||||
Debug bool // -d
|
||||
Output string // -o <mode>
|
||||
Clusters map[string]*Cluster
|
||||
DefaultCluster *Cluster
|
||||
Index string // index: -i
|
||||
Failed, Partials bool // index: flags
|
||||
Shards, Replicas int // index create+allocation: -s -r
|
||||
Wait bool // index create: -w
|
||||
Primary bool // index allocation: -p
|
||||
Searchable bool // index fields: -s
|
||||
Aggretable bool // index fields: -a
|
||||
|
||||
Priority int // index template create: -p
|
||||
Settings []string // index template create: -s
|
||||
Patterns []string // index template create: -i
|
||||
Components []string // index template create: -c
|
||||
Meta []string // index template create: -M
|
||||
Aliases []string // index template create: -A
|
||||
Stream bool // index template create: -S
|
||||
AutoCreate bool // index template create: -a
|
||||
Mode string // index template create: -a
|
||||
Retention string // index template create: -r
|
||||
|
||||
From, To, MaxItems int // search: flags
|
||||
Filter []string // search: -F
|
||||
Path string // search+doc sh: -p
|
||||
@@ -75,7 +87,7 @@ type Config struct {
|
||||
Ascending bool // sort: -a
|
||||
Exclude string // cluster compare: -e (regexp)
|
||||
All, Verbose bool // cluster status: -a -v
|
||||
Persistent, Transient, Default bool // -p -t -D cluster settings set
|
||||
Persistent, Transient, Default bool // cluster settings set: -p -t -D
|
||||
Force bool // ccr follower renew: -f
|
||||
HaveJQ bool // determined at runtime by ourselfes
|
||||
DebugHTTP bool // root: --debug-http
|
||||
|
||||
@@ -27,36 +27,6 @@ import (
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
// recursively traverse the raw settings hash and build a flat map
|
||||
// consisting of the translated path and its value.
|
||||
//
|
||||
// e.g.
|
||||
// logger:
|
||||
//
|
||||
// org:
|
||||
// elasticsearch:
|
||||
// transport:
|
||||
// OutboundHandler: "ERROR"
|
||||
//
|
||||
// gets:
|
||||
//
|
||||
// logger.org.elasticsearch.transport.OutboundHandler: "ERROR"
|
||||
func getJsonPath(raw map[string]any, topic string) map[string]string {
|
||||
paths := map[string]string{}
|
||||
|
||||
for name, data := range raw {
|
||||
path := topic + "." + name
|
||||
switch value := data.(type) {
|
||||
case string:
|
||||
paths[path] = value
|
||||
case map[string]any:
|
||||
paths = getJsonPath(value, path)
|
||||
}
|
||||
}
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
func ClusterSettingsList(conf *cfg.Config) error {
|
||||
res, err := conf.DefaultCluster.ES.Cluster.GetSettings().
|
||||
Header("content-type", "application/json").
|
||||
@@ -87,7 +57,7 @@ func ClusterSettingsList(conf *cfg.Config) error {
|
||||
return fmt.Errorf("failed to unmarshall setting for topic %s: %s", topic, err)
|
||||
}
|
||||
|
||||
paths := getJsonPath(data, topic)
|
||||
paths := getJsonPath(map[string]string{}, data, topic)
|
||||
slog.Debug("settings", topic, paths)
|
||||
|
||||
for setting, value := range paths {
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -397,3 +398,44 @@ func getClusterData(es *elasticsearch.TypedClient, wg *sync.WaitGroup, reschan c
|
||||
|
||||
reschan <- ar
|
||||
}
|
||||
|
||||
// recursively traverse the raw settings hash and build a flat map
|
||||
// consisting of the translated path and its value.
|
||||
//
|
||||
// e.g.
|
||||
// logger:
|
||||
//
|
||||
// org:
|
||||
// elasticsearch:
|
||||
// transport:
|
||||
// OutboundHandler: "ERROR"
|
||||
//
|
||||
// gets:
|
||||
//
|
||||
// logger.org.elasticsearch.transport.OutboundHandler: "ERROR"
|
||||
func getJsonPath(paths map[string]string, raw map[string]any, topic string) map[string]string {
|
||||
for name, data := range raw {
|
||||
path := topic + "." + name
|
||||
|
||||
switch value := data.(type) {
|
||||
case string:
|
||||
paths[path] = value
|
||||
case *string:
|
||||
paths[path] = *value
|
||||
case int:
|
||||
paths[path] = strconv.Itoa(value)
|
||||
case *int:
|
||||
paths[path] = strconv.Itoa(*value)
|
||||
case map[string]any:
|
||||
paths = getJsonPath(paths, value, path)
|
||||
case []any:
|
||||
val := []string{}
|
||||
for _, item := range value {
|
||||
val = append(val, fmt.Sprintf("%v", item))
|
||||
}
|
||||
paths[path] = strings.Join(val, ",")
|
||||
}
|
||||
}
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
@@ -126,11 +126,7 @@ func IndexList(conf *cfg.Config) error {
|
||||
}
|
||||
|
||||
table.Sort()
|
||||
if err := table.Print(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return table.Print()
|
||||
}
|
||||
|
||||
func IndexShow(conf *cfg.Config, indexpattern string) error {
|
||||
|
||||
449
pkg/es/index_template.go
Normal file
449
pkg/es/index_template.go
Normal file
@@ -0,0 +1,449 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package es
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"codeberg.org/scip/esctl/pkg/cfg"
|
||||
"codeberg.org/scip/esctl/pkg/printer"
|
||||
"github.com/elastic/go-elasticsearch/v9/typedapi/esdsl"
|
||||
"github.com/elastic/go-elasticsearch/v9/typedapi/types"
|
||||
)
|
||||
|
||||
// used for completion
|
||||
func IndexTemplateList(conf *cfg.Config) error {
|
||||
res, err := conf.DefaultCluster.ES.Indices.GetIndexTemplate().
|
||||
Header("content-type", "application/json").
|
||||
Header("accept", "application/json").
|
||||
Do(context.Background())
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get index templates: %s", esErrorString(err))
|
||||
}
|
||||
|
||||
slog.Debug("res", "index templates", res)
|
||||
|
||||
table := printer.NewTable(conf, 5, len(res.IndexTemplates))
|
||||
table.Addheaders("name", "description", "priority")
|
||||
|
||||
for idx, tpl := range res.IndexTemplates {
|
||||
desc, err := json.Marshal(tpl.IndexTemplate.Meta_["description"])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal meta json data: %w", err)
|
||||
}
|
||||
|
||||
table.Entries[idx] = []string{
|
||||
tpl.Name,
|
||||
string(desc),
|
||||
fmt.Sprintf("%d", tpl.IndexTemplate.Priority),
|
||||
}
|
||||
}
|
||||
|
||||
table.Sort()
|
||||
return table.Print()
|
||||
}
|
||||
|
||||
func IndexTemplateShow(conf *cfg.Config, tplname string) error {
|
||||
res, err := conf.DefaultCluster.ES.Indices.GetIndexTemplate().
|
||||
Name(tplname).
|
||||
Header("content-type", "application/json").
|
||||
Header("accept", "application/json").
|
||||
Do(context.Background())
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get index template: %s", esErrorString(err))
|
||||
}
|
||||
|
||||
slog.Debug("res", "index template", res)
|
||||
|
||||
if len(res.IndexTemplates) != 1 {
|
||||
return errors.New("multiple or no index template matched the pattern")
|
||||
}
|
||||
|
||||
tpl := res.IndexTemplates[0]
|
||||
|
||||
table := printer.NewTable(conf, 2, 6)
|
||||
table.Addheaders("index template property", "value")
|
||||
|
||||
desc, err := json.Marshal(tpl.IndexTemplate.Meta_["description"])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal meta json data: %w", err)
|
||||
}
|
||||
|
||||
hasds := tpl.IndexTemplate.DataStream != nil
|
||||
|
||||
aliases := []string{}
|
||||
for alias := range tpl.IndexTemplate.Template.Aliases {
|
||||
aliases = append(aliases, alias)
|
||||
}
|
||||
|
||||
table.Entries = [][]string{
|
||||
{"name", tpl.Name},
|
||||
{"description", string(desc)},
|
||||
{"index patterns", strings.Join(tpl.IndexTemplate.IndexPatterns, ",")},
|
||||
{"composed of", strings.Join(tpl.IndexTemplate.ComposedOf, ",")},
|
||||
{"data stream enabled", fmt.Sprintf("%t", hasds)},
|
||||
{"aliases", strings.Join(aliases, ",")},
|
||||
}
|
||||
|
||||
if err := table.Print(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
table = printer.NewTable(conf, 2, 0)
|
||||
table.Addheaders("index setting property", "value")
|
||||
|
||||
err = getIndexTemplateSettings(conf, tplname, table)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
if err := table.Print(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
table = printer.NewTable(conf, 2, 0)
|
||||
table.Addheaders("index field mapping", "type")
|
||||
|
||||
for name, field := range tpl.IndexTemplate.Template.Mappings.Properties {
|
||||
typeval := ""
|
||||
|
||||
switch val := field.(type) {
|
||||
case *types.IntegerNumberProperty:
|
||||
typeval = val.Type
|
||||
case *types.KeywordProperty:
|
||||
typeval = val.Type
|
||||
case *types.DateProperty:
|
||||
typeval = val.Type
|
||||
}
|
||||
|
||||
table.Entries = append(table.Entries, []string{
|
||||
name, typeval,
|
||||
})
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
if err := table.Print(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func IndexTemplateCreate(conf *cfg.Config, name string, mappings []string) error {
|
||||
settings := esdsl.NewIndexSettings()
|
||||
maps := esdsl.NewIndexTemplateMapping()
|
||||
|
||||
create := conf.DefaultCluster.ES.Indices.PutIndexTemplate(name).
|
||||
Header("content-type", "application/json").
|
||||
Header("accept", "application/json")
|
||||
|
||||
if conf.Shards > 0 {
|
||||
settings = settings.NumberOfShards(strconv.Itoa(conf.Shards))
|
||||
}
|
||||
|
||||
if conf.Replicas > 0 {
|
||||
settings = settings.NumberOfReplicas(strconv.Itoa(conf.Replicas))
|
||||
}
|
||||
|
||||
if conf.Stream {
|
||||
create.DataStream(esdsl.NewDataStreamVisibility())
|
||||
|
||||
if conf.Retention != "" {
|
||||
maps.Lifecycle(
|
||||
esdsl.NewDataStreamLifecycle().
|
||||
DataRetention(
|
||||
esdsl.NewDuration().String(conf.Retention)))
|
||||
}
|
||||
}
|
||||
|
||||
if conf.AutoCreate {
|
||||
create.AllowAutoCreate(true)
|
||||
}
|
||||
|
||||
if conf.Mode != "" {
|
||||
if !slices.Contains([]string{"standard", "timeseries", "logsdb", "lookup"}, conf.Mode) {
|
||||
return errors.New("mode must be one of: standard, timeseries, logsdb or lookup")
|
||||
}
|
||||
|
||||
settings = settings.Mode(conf.Mode)
|
||||
}
|
||||
|
||||
if len(mappings) > 0 {
|
||||
typemaps := esdsl.NewTypeMapping()
|
||||
|
||||
for _, mapping := range mappings {
|
||||
parts := strings.Split(mapping, ":")
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf(
|
||||
"invalid mapping %s, expect <name:type> (type: integer, text, date, keyword)",
|
||||
mapping)
|
||||
}
|
||||
|
||||
switch parts[1] {
|
||||
case "text":
|
||||
typemaps.AddProperty(parts[0], esdsl.NewTextProperty())
|
||||
case "integer":
|
||||
typemaps.AddProperty(parts[0], esdsl.NewIntegerNumberProperty())
|
||||
case "date":
|
||||
typemaps.AddProperty(parts[0], esdsl.NewDateProperty())
|
||||
case "keyword":
|
||||
typemaps.AddProperty(parts[0], esdsl.NewKeywordProperty())
|
||||
}
|
||||
}
|
||||
|
||||
maps.Mappings(typemaps)
|
||||
}
|
||||
|
||||
if len(conf.Components) > 0 {
|
||||
create.ComposedOf(conf.Components...)
|
||||
}
|
||||
|
||||
for _, alias := range conf.Aliases {
|
||||
maps.AddAlias(alias, esdsl.NewAlias())
|
||||
}
|
||||
|
||||
if len(conf.Meta) > 0 {
|
||||
metadata := map[string]json.RawMessage{}
|
||||
|
||||
for _, meta := range conf.Meta {
|
||||
parts := strings.Split(meta, ":")
|
||||
if len(parts) != 2 {
|
||||
return errors.New("meta data must be in the form key:value")
|
||||
}
|
||||
|
||||
msg, err := json.Marshal(parts[1])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to json marshal metadata %s: %w", meta, err)
|
||||
}
|
||||
|
||||
metadata[parts[0]] = msg
|
||||
}
|
||||
|
||||
create.Meta_(esdsl.NewMetadata(metadata))
|
||||
}
|
||||
|
||||
if len(conf.Settings) > 0 {
|
||||
usersettings := map[string]json.RawMessage{}
|
||||
|
||||
for _, meta := range conf.Settings {
|
||||
parts := strings.Split(meta, ":")
|
||||
if len(parts) != 2 {
|
||||
return errors.New("settings data must be in the form key:value")
|
||||
}
|
||||
|
||||
msg, err := json.Marshal(parts[1])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to json marshal metadata %s: %w", meta, err)
|
||||
}
|
||||
|
||||
usersettings[parts[0]] = msg
|
||||
}
|
||||
|
||||
settings = settings.IndexSettings(usersettings)
|
||||
}
|
||||
|
||||
maps.Settings(settings)
|
||||
create.Template(maps)
|
||||
create.IndexPatterns(conf.Patterns...)
|
||||
|
||||
_, err := create.Do(context.Background())
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create index template: %s", esErrorString(err))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FIXME: func's too long, refactor
|
||||
// FIXME: it's not yet possible to remove items in lists (aliases, mappings, settings), just overwrite them
|
||||
func IndexTemplateModify(conf *cfg.Config, name string, mappings []string) error {
|
||||
maps := esdsl.NewIndexTemplateMapping()
|
||||
|
||||
// load existing index mapping
|
||||
res, err := conf.DefaultCluster.ES.Indices.GetIndexTemplate().
|
||||
Name(name).
|
||||
Header("content-type", "application/json").
|
||||
Header("accept", "application/json").
|
||||
Do(context.Background())
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get index template: %s", esErrorString(err))
|
||||
}
|
||||
|
||||
slog.Debug("res", "index template", res)
|
||||
|
||||
if len(res.IndexTemplates) != 1 {
|
||||
return errors.New("multiple or no index template matched the pattern")
|
||||
}
|
||||
|
||||
tpl := res.IndexTemplates[0]
|
||||
|
||||
// our modify PUT request
|
||||
modify := conf.DefaultCluster.ES.Indices.PutIndexTemplate(name).
|
||||
Header("content-type", "application/json").
|
||||
Header("accept", "application/json")
|
||||
|
||||
// load existing settings, if any
|
||||
settings := tpl.IndexTemplate.Template.Settings
|
||||
|
||||
// pre fill mappings and aliases
|
||||
maps.Mappings(tpl.IndexTemplate.Template.Mappings)
|
||||
maps.Aliases(tpl.IndexTemplate.Template.Aliases)
|
||||
|
||||
// pre fill meta, if any
|
||||
metadata := map[string]json.RawMessage{}
|
||||
for key, value := range tpl.IndexTemplate.Meta_ {
|
||||
metadata[key] = value
|
||||
}
|
||||
|
||||
// pre fill components
|
||||
modify.ComposedOf(tpl.IndexTemplate.ComposedOf...)
|
||||
|
||||
if conf.Stream {
|
||||
modify.DataStream(esdsl.NewDataStreamVisibility())
|
||||
|
||||
if conf.Retention != "" {
|
||||
maps.Lifecycle(
|
||||
esdsl.NewDataStreamLifecycle().
|
||||
DataRetention(
|
||||
esdsl.NewDuration().String(conf.Retention)))
|
||||
}
|
||||
}
|
||||
|
||||
modify.AllowAutoCreate(conf.AutoCreate)
|
||||
|
||||
if conf.Mode != "" {
|
||||
if !slices.Contains([]string{"standard", "timeseries", "logsdb", "lookup"}, conf.Mode) {
|
||||
return errors.New("mode must be one of: standard, timeseries, logsdb or lookup")
|
||||
}
|
||||
|
||||
*settings.Mode = conf.Mode
|
||||
}
|
||||
|
||||
if len(mappings) > 0 {
|
||||
typemaps := esdsl.NewTypeMapping()
|
||||
|
||||
for _, mapping := range mappings {
|
||||
parts := strings.Split(mapping, ":")
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf(
|
||||
"invalid mapping %s, expect <name:type> (type: integer, text, date, keyword)",
|
||||
mapping)
|
||||
}
|
||||
|
||||
switch parts[1] {
|
||||
case "text":
|
||||
typemaps.AddProperty(parts[0], esdsl.NewTextProperty())
|
||||
case "integer":
|
||||
typemaps.AddProperty(parts[0], esdsl.NewIntegerNumberProperty())
|
||||
case "date":
|
||||
typemaps.AddProperty(parts[0], esdsl.NewDateProperty())
|
||||
case "keyword":
|
||||
typemaps.AddProperty(parts[0], esdsl.NewKeywordProperty())
|
||||
}
|
||||
}
|
||||
|
||||
maps.Mappings(typemaps)
|
||||
}
|
||||
|
||||
if len(conf.Components) > 0 {
|
||||
modify.ComposedOf(conf.Components...)
|
||||
}
|
||||
|
||||
for _, alias := range conf.Aliases {
|
||||
maps.AddAlias(alias, esdsl.NewAlias())
|
||||
}
|
||||
|
||||
if len(conf.Meta) > 0 {
|
||||
for _, meta := range conf.Meta {
|
||||
parts := strings.Split(meta, ":")
|
||||
if len(parts) != 2 {
|
||||
return errors.New("meta data must be in the form key:value")
|
||||
}
|
||||
|
||||
msg, err := json.Marshal(parts[1])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to json marshal metadata %s: %w", meta, err)
|
||||
}
|
||||
|
||||
metadata[parts[0]] = msg
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
modify.Meta_(esdsl.NewMetadata(metadata))
|
||||
|
||||
if len(conf.Settings) > 0 {
|
||||
usersettings := map[string]json.RawMessage{}
|
||||
|
||||
for _, meta := range conf.Settings {
|
||||
parts := strings.Split(meta, ":")
|
||||
if len(parts) != 2 {
|
||||
return errors.New("settings data must be in the form key:value")
|
||||
}
|
||||
|
||||
msg, err := json.Marshal(parts[1])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to json marshal metadata %s: %w", meta, err)
|
||||
}
|
||||
|
||||
usersettings[parts[0]] = msg
|
||||
}
|
||||
|
||||
settings.IndexSettings = usersettings
|
||||
}
|
||||
|
||||
patterns := tpl.IndexTemplate.IndexPatterns
|
||||
if len(conf.Patterns) > 0 {
|
||||
patterns = conf.Patterns
|
||||
}
|
||||
|
||||
maps.Settings(settings)
|
||||
modify.Template(maps)
|
||||
modify.IndexPatterns(patterns...)
|
||||
|
||||
_, err = modify.Do(context.Background())
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to modify index template: %s", esErrorString(err))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func IndexTemplateDelete(conf *cfg.Config, name string) error {
|
||||
_, err := conf.DefaultCluster.ES.Indices.DeleteIndexTemplate(name).
|
||||
Header("content-type", "application/json").
|
||||
Header("accept", "application/json").
|
||||
Do(context.Background())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete index template: %s", esErrorString(err))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
61
pkg/es/index_template_settings.go
Normal file
61
pkg/es/index_template_settings.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package es
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"codeberg.org/scip/esctl/pkg/cfg"
|
||||
"codeberg.org/scip/esctl/pkg/printer"
|
||||
)
|
||||
|
||||
type TplTemplateData struct {
|
||||
Settings map[string]any `json:"settings"`
|
||||
Aliases map[string]any `json:"aliases"`
|
||||
Lifecycle map[string]any `json:"lifecycle"`
|
||||
Mappings map[string]any `json:"mappings"`
|
||||
}
|
||||
|
||||
type TplTemplate struct {
|
||||
Template TplTemplateData `json:"template"`
|
||||
}
|
||||
|
||||
type TplTpl struct {
|
||||
IndexTemplate TplTemplate `json:"index_template"`
|
||||
}
|
||||
|
||||
type Tpl struct {
|
||||
IndexTemplates []TplTpl `json:"index_templates"`
|
||||
}
|
||||
|
||||
func getIndexTemplateSettings(conf *cfg.Config, tplname string, table *printer.Table) error {
|
||||
raw, err := CallAPI(conf, "GET", "/_index_template/"+tplname, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := Tpl{}
|
||||
if err = json.Unmarshal(raw, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if conf.Debug {
|
||||
if err := prettyfiJson(conf, raw); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(data.IndexTemplates) == 0 {
|
||||
return fmt.Errorf("index_template %s not found", tplname)
|
||||
}
|
||||
|
||||
tpl := data.IndexTemplates[0].IndexTemplate.Template.Settings
|
||||
for topic, val := range tpl {
|
||||
paths := getJsonPath(map[string]string{}, val.(map[string]any), topic)
|
||||
|
||||
for setting, value := range paths {
|
||||
table.Entries = append(table.Entries, []string{setting, fmt.Sprintf("%v", value)})
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -110,12 +110,14 @@ func Repl(conf *cfg.Config) error {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
err = CallAPI(conf, parts[0], parts[1], data)
|
||||
raw, err := CallAPI(conf, parts[0], parts[1], data)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to call API: %s\n", esErrorString(err))
|
||||
}
|
||||
|
||||
reader.SetPrompt("> ")
|
||||
if err := prettyfiJson(conf, raw); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -125,7 +127,7 @@ func encodeAuth(username, password string) string {
|
||||
return base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
|
||||
}
|
||||
|
||||
func CallAPI(conf *cfg.Config, verb, path, data string) error {
|
||||
func CallAPI(conf *cfg.Config, verb, path, data string) ([]byte, error) {
|
||||
verb = strings.ToUpper(verb)
|
||||
|
||||
// we're using port-forwards anyway
|
||||
@@ -137,7 +139,7 @@ func CallAPI(conf *cfg.Config, verb, path, data string) error {
|
||||
|
||||
req, err := http.NewRequest(verb, conf.DefaultCluster.Uri+path, bytes.NewBuffer([]byte(data)))
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
@@ -147,16 +149,16 @@ func CallAPI(conf *cfg.Config, verb, path, data string) error {
|
||||
// actually execute the request
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Read and print response
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read response body: %s", err)
|
||||
return nil, fmt.Errorf("failed to read response body: %s", err)
|
||||
}
|
||||
|
||||
return prettyfiJson(conf, body)
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func prettyfiJson(conf *cfg.Config, raw []byte) error {
|
||||
|
||||
Reference in New Issue
Block a user