diff --git a/cmd/esmapping-generator/generator/esmapping_generator.go b/cmd/esmapping-generator/generator/esmapping_generator.go new file mode 100644 index 00000000000..6b3f346fdc8 --- /dev/null +++ b/cmd/esmapping-generator/generator/esmapping_generator.go @@ -0,0 +1,26 @@ +// Copyright (c) 2025 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package generator + +import ( + "fmt" + + "github.com/jaegertracing/jaeger/cmd/esmapping-generator/app" + "github.com/jaegertracing/jaeger/cmd/esmapping-generator/app/renderer" + "github.com/jaegertracing/jaeger/pkg/es" + "github.com/jaegertracing/jaeger/plugin/storage/es/mappings" +) + +func GenerateMappings(options app.Options) (string, error) { + if _, err := mappings.MappingTypeFromString(options.Mapping); err != nil { + return "", fmt.Errorf("invalid mapping type '%s': please pass either 'jaeger-service' or 'jaeger-span' as the mapping type %w", options.Mapping, err) + } + + parsedMapping, err := renderer.GetMappingAsString(es.TextTemplateBuilder{}, &options) + if err != nil { + return "", fmt.Errorf("failed to render mapping to string: %w", err) + } + + return parsedMapping, nil +} diff --git a/cmd/esmapping-generator/generator/esmapping_generator_test.go b/cmd/esmapping-generator/generator/esmapping_generator_test.go new file mode 100644 index 00000000000..3c3f237c80c --- /dev/null +++ b/cmd/esmapping-generator/generator/esmapping_generator_test.go @@ -0,0 +1,87 @@ +// Copyright (c) 2025 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package generator + +import ( + "encoding/json" + "testing" + + "github.com/crossdock/crossdock-go/assert" + "github.com/crossdock/crossdock-go/require" + + "github.com/jaegertracing/jaeger/cmd/esmapping-generator/app" + "github.com/jaegertracing/jaeger/pkg/testutils" +) + +func TestGenerateMappings(t *testing.T) { + tests := []struct { + name string + options app.Options + expectErr bool + }{ + { + name: "valid jaeger-span mapping", + options: app.Options{ + Mapping: "jaeger-span", + EsVersion: 7, + Shards: 5, + Replicas: 1, + IndexPrefix: "jaeger-index", + UseILM: "false", + ILMPolicyName: "jaeger-ilm-policy", + }, + expectErr: false, + }, + { + name: "valid jaeger-service mapping", + options: app.Options{ + Mapping: "jaeger-service", + EsVersion: 7, + Shards: 5, + Replicas: 1, + IndexPrefix: "jaeger-service-index", + UseILM: "true", + ILMPolicyName: "service-ilm-policy", + }, + expectErr: false, + }, + { + name: "invalid mapping type", + options: app.Options{ + Mapping: "invalid-mapping", + }, + expectErr: true, + }, + { + name: "missing mapping flag", + options: app.Options{ + Mapping: "", + }, + expectErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := GenerateMappings(tt.options) + if tt.expectErr { + require.Error(t, err, "Expected an error") + } else { + require.NoError(t, err, "Did not expect an error") + + var parsed map[string]interface{} + err = json.Unmarshal([]byte(result), &parsed) + require.NoError(t, err, "Expected valid JSON output") + + assert.NotEmpty(t, parsed["index_patterns"], "Expected index_patterns to be present") + assert.NotEmpty(t, parsed["mappings"], "Expected mappings to be present") + assert.NotEmpty(t, parsed["settings"], "Expected settings to be present") + } + }) + } +} + +func TestMain(m *testing.M) { + testutils.VerifyGoLeaks(m) +} diff --git a/cmd/esmapping-generator/main.go b/cmd/esmapping-generator/main.go index 84005602332..12fdeade8f7 100644 --- a/cmd/esmapping-generator/main.go +++ b/cmd/esmapping-generator/main.go @@ -7,41 +7,15 @@ import ( "fmt" "os" - "github.com/spf13/cobra" - "go.uber.org/zap" - - "github.com/jaegertracing/jaeger/cmd/esmapping-generator/app" - "github.com/jaegertracing/jaeger/cmd/esmapping-generator/app/renderer" - "github.com/jaegertracing/jaeger/pkg/es" + "github.com/jaegertracing/jaeger/internal/storage/elasticsearch/mapping" "github.com/jaegertracing/jaeger/pkg/version" - "github.com/jaegertracing/jaeger/plugin/storage/es/mappings" ) func main() { - logger, _ := zap.NewDevelopment() - options := app.Options{} - command := &cobra.Command{ - Use: "jaeger-esmapping-generator", - Short: "Jaeger esmapping-generator prints rendered mappings as string", - Long: `Jaeger esmapping-generator renders passed templates with provided values and prints rendered output to stdout`, - Run: func(_ *cobra.Command, _ /* args */ []string) { - if _, err := mappings.MappingTypeFromString(options.Mapping); err != nil { - logger.Fatal("please pass either 'jaeger-service' or 'jaeger-span' as argument") - } - - parsedMapping, err := renderer.GetMappingAsString(es.TextTemplateBuilder{}, &options) - if err != nil { - logger.Fatal(err.Error()) - } - print(parsedMapping) - }, - } - - options.AddFlags(command) - - command.AddCommand(version.Command()) + esmappingsCmd := mapping.Command() + esmappingsCmd.AddCommand(version.Command()) - if err := command.Execute(); err != nil { + if err := esmappingsCmd.Execute(); err != nil { fmt.Println(err.Error()) os.Exit(1) } diff --git a/cmd/jaeger/main.go b/cmd/jaeger/main.go index d63268845e7..30a50ebdf54 100644 --- a/cmd/jaeger/main.go +++ b/cmd/jaeger/main.go @@ -10,6 +10,7 @@ import ( "github.com/jaegertracing/jaeger/cmd/internal/docs" "github.com/jaegertracing/jaeger/cmd/jaeger/internal" + "github.com/jaegertracing/jaeger/internal/storage/elasticsearch/mapping" "github.com/jaegertracing/jaeger/pkg/config" "github.com/jaegertracing/jaeger/pkg/version" ) @@ -19,6 +20,7 @@ func main() { command := internal.Command() command.AddCommand(version.Command()) command.AddCommand(docs.Command(v)) + command.AddCommand(mapping.Command()) config.AddFlags( v, command, diff --git a/internal/storage/elasticsearch/mapping/command.go b/internal/storage/elasticsearch/mapping/command.go new file mode 100644 index 00000000000..c0f8bcd89bb --- /dev/null +++ b/internal/storage/elasticsearch/mapping/command.go @@ -0,0 +1,33 @@ +// Copyright (c) 2025 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package mapping + +import ( + "fmt" + "log" + + "github.com/spf13/cobra" + + "github.com/jaegertracing/jaeger/cmd/esmapping-generator/app" + "github.com/jaegertracing/jaeger/cmd/esmapping-generator/generator" +) + +func Command() *cobra.Command { + options := app.Options{} + command := &cobra.Command{ + Use: "elasticsearch-mappings", + Short: "Jaeger esmapping-generator prints rendered mappings as string", + Long: "Jaeger esmapping-generator renders passed templates with provided values and prints rendered output to stdout", + Run: func(_ *cobra.Command, _ /* args */ []string) { + result, err := generator.GenerateMappings(options) + if err != nil { + log.Fatalf("Error generating mappings: %v", err) + } + fmt.Println(result) + }, + } + options.AddFlags(command) + + return command +} diff --git a/internal/storage/elasticsearch/mapping/command_test.go b/internal/storage/elasticsearch/mapping/command_test.go new file mode 100644 index 00000000000..ce45fe2cceb --- /dev/null +++ b/internal/storage/elasticsearch/mapping/command_test.go @@ -0,0 +1,51 @@ +// Copyright (c) 2025 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package mapping + +import ( + "encoding/json" + "os" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/jaegertracing/jaeger/pkg/testutils" +) + +func TestCommandExecute(t *testing.T) { + cmd := Command() + + // TempFile to capture output + tempFile, err := os.CreateTemp("", "command-output-*.txt") + require.NoError(t, err) + defer os.Remove(tempFile.Name()) + + // Redirect stdout to the TempFile + oldStdout := os.Stdout + os.Stdout = tempFile + defer func() { os.Stdout = oldStdout }() + + err = cmd.ParseFlags([]string{ + "--mapping=jaeger-span", + "--es-version=7", + "--shards=5", + "--replicas=1", + "--index-prefix=jaeger-index", + "--use-ilm=false", + "--ilm-policy-name=jaeger-ilm-policy", + }) + require.NoError(t, err) + require.NoError(t, cmd.Execute()) + + output, err := os.ReadFile(tempFile.Name()) + require.NoError(t, err) + + var jsonOutput map[string]any + err = json.Unmarshal(output, &jsonOutput) + require.NoError(t, err, "Output should be valid JSON") +} + +func TestMain(m *testing.M) { + testutils.VerifyGoLeaks(m) +}