From 84fa3dcc8b2268293c0bddc1769c8a471ded5b0d Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Wed, 26 Nov 2025 17:49:48 +0100 Subject: [PATCH 1/2] Add configuration setting to configure ECS urls --- cmd/build.go | 7 +++ internal/builder/external_fields.go | 4 +- internal/builder/packages.go | 4 +- internal/fields/dependency_manager.go | 41 ++++++++++--- internal/fields/dependency_manager_test.go | 59 +++++++++++++++++++ internal/fields/validate.go | 16 ++++- internal/install/application_configuration.go | 10 ++++ internal/packages/installer/factory.go | 3 + 8 files changed, 129 insertions(+), 15 deletions(-) diff --git a/cmd/build.go b/cmd/build.go index ec814a9d57..0f16f84ebb 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -13,6 +13,7 @@ import ( "github.com/elastic/elastic-package/internal/builder" "github.com/elastic/elastic-package/internal/cobraext" "github.com/elastic/elastic-package/internal/files" + "github.com/elastic/elastic-package/internal/install" "github.com/elastic/elastic-package/internal/logger" "github.com/elastic/elastic-package/internal/packages" ) @@ -78,6 +79,11 @@ func buildCommandAction(cmd *cobra.Command, args []string) error { } logger.Debugf("Use build directory: %s", buildDir) + config, err := install.Configuration() + if err != nil { + return fmt.Errorf("can't load configuration: %w", err) + } + target, err := builder.BuildPackage(builder.BuildOptions{ PackageRoot: packageRoot, BuildDir: buildDir, @@ -86,6 +92,7 @@ func buildCommandAction(cmd *cobra.Command, args []string) error { SkipValidation: skipValidation, RepositoryRoot: repositoryRoot, UpdateReadmes: true, + SchemaURLs: config.SchemaURLs(), }) if err != nil { return fmt.Errorf("building package failed: %w", err) diff --git a/internal/builder/external_fields.go b/internal/builder/external_fields.go index e7daaef01e..ae0818ed82 100644 --- a/internal/builder/external_fields.go +++ b/internal/builder/external_fields.go @@ -22,7 +22,7 @@ import ( var semver3_0_0 = semver.MustParse("3.0.0") -func resolveExternalFields(packageRoot, buildPackageRoot string) error { +func resolveExternalFields(packageRoot, buildPackageRoot string, schemaURLs fields.SchemaURLs) error { bm, ok, err := buildmanifest.ReadBuildManifest(packageRoot) if err != nil { return fmt.Errorf("can't read build manifest: %w", err) @@ -37,7 +37,7 @@ func resolveExternalFields(packageRoot, buildPackageRoot string) error { } logger.Debugf("Package has external dependencies defined") - fdm, err := fields.CreateFieldDependencyManager(bm.Dependencies) + fdm, err := fields.CreateFieldDependencyManager(bm.Dependencies, schemaURLs) if err != nil { return fmt.Errorf("can't create field dependency manager: %w", err) } diff --git a/internal/builder/packages.go b/internal/builder/packages.go index bce45095c2..711c595384 100644 --- a/internal/builder/packages.go +++ b/internal/builder/packages.go @@ -14,6 +14,7 @@ import ( "github.com/elastic/elastic-package/internal/docs" "github.com/elastic/elastic-package/internal/environment" + "github.com/elastic/elastic-package/internal/fields" "github.com/elastic/elastic-package/internal/files" "github.com/elastic/elastic-package/internal/logger" "github.com/elastic/elastic-package/internal/packages" @@ -34,6 +35,7 @@ type BuildOptions struct { SignPackage bool SkipValidation bool UpdateReadmes bool + SchemaURLs fields.SchemaURLs } // BuildDirectory function locates the target build directory. If the directory doesn't exist, it will create it. @@ -215,7 +217,7 @@ func BuildPackage(options BuildOptions) (string, error) { } logger.Debug("Resolve external fields") - err = resolveExternalFields(options.PackageRoot, buildPackageRoot) + err = resolveExternalFields(options.PackageRoot, buildPackageRoot, options.SchemaURLs) if err != nil { return "", fmt.Errorf("resolving external fields failed: %w", err) } diff --git a/internal/fields/dependency_manager.go b/internal/fields/dependency_manager.go index 9f783ea345..c54e252b70 100644 --- a/internal/fields/dependency_manager.go +++ b/internal/fields/dependency_manager.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "net/http" + "net/url" "os" "path/filepath" "strings" @@ -27,7 +28,6 @@ const ( localFilePrefix = "file://" ecsSchemaFile = "ecs_nested.yml" - ecsSchemaURL = "https://raw.githubusercontent.com/elastic/ecs/%s/generated/ecs/%s" ) // DependencyManager is responsible for resolving external field dependencies. @@ -35,9 +35,13 @@ type DependencyManager struct { schema map[string][]FieldDefinition } +type SchemaURLs struct { + ECSBase string `yaml:"ecs_base,omitempty"` +} + // CreateFieldDependencyManager function creates a new instance of the DependencyManager. -func CreateFieldDependencyManager(deps buildmanifest.Dependencies) (*DependencyManager, error) { - schema, err := buildFieldsSchema(deps) +func CreateFieldDependencyManager(deps buildmanifest.Dependencies, urls SchemaURLs) (*DependencyManager, error) { + schema, err := buildFieldsSchema(deps, urls) if err != nil { return nil, fmt.Errorf("can't build fields schema: %w", err) } @@ -46,9 +50,9 @@ func CreateFieldDependencyManager(deps buildmanifest.Dependencies) (*DependencyM }, nil } -func buildFieldsSchema(deps buildmanifest.Dependencies) (map[string][]FieldDefinition, error) { +func buildFieldsSchema(deps buildmanifest.Dependencies, urls SchemaURLs) (map[string][]FieldDefinition, error) { schema := map[string][]FieldDefinition{} - ecsSchema, err := loadECSFieldsSchema(deps.ECS) + ecsSchema, err := loadECSFieldsSchema(deps.ECS, urls.ECSBase) if err != nil { return nil, fmt.Errorf("can't load fields: %w", err) } @@ -56,13 +60,13 @@ func buildFieldsSchema(deps buildmanifest.Dependencies) (map[string][]FieldDefin return schema, nil } -func loadECSFieldsSchema(dep buildmanifest.ECSDependency) ([]FieldDefinition, error) { +func loadECSFieldsSchema(dep buildmanifest.ECSDependency, baseURL string) ([]FieldDefinition, error) { if dep.Reference == "" { logger.Debugf("ECS dependency isn't defined") return nil, nil } - content, err := readECSFieldsSchemaFile(dep) + content, err := readECSFieldsSchemaFile(dep, baseURL) if err != nil { return nil, fmt.Errorf("error reading ECS fields schema file: %w", err) } @@ -70,7 +74,7 @@ func loadECSFieldsSchema(dep buildmanifest.ECSDependency) ([]FieldDefinition, er return parseECSFieldsSchema(content) } -func readECSFieldsSchemaFile(dep buildmanifest.ECSDependency) ([]byte, error) { +func readECSFieldsSchemaFile(dep buildmanifest.ECSDependency, baseURL string) ([]byte, error) { if strings.HasPrefix(dep.Reference, localFilePrefix) { path := strings.TrimPrefix(dep.Reference, localFilePrefix) return os.ReadFile(path) @@ -90,7 +94,10 @@ func readECSFieldsSchemaFile(dep buildmanifest.ECSDependency) ([]byte, error) { if errors.Is(err, os.ErrNotExist) { logger.Debugf("Pulling ECS dependency using reference: %s", dep.Reference) - url := fmt.Sprintf(ecsSchemaURL, gitReference, ecsSchemaFile) + url, err := ecsSchemaURL(baseURL, gitReference, ecsSchemaFile) + if err != nil { + return nil, fmt.Errorf("can't generate ECS schema URL: %w", err) + } logger.Debugf("Schema URL: %s", url) resp, err := http.Get(url) if err != nil { @@ -166,6 +173,22 @@ func asGitReference(reference string) (string, error) { return reference[len(gitReferencePrefix):], nil } +const defaultECSSchemaBaseURL = "https://raw.githubusercontent.com/elastic/ecs" + +func ecsSchemaURL(baseURL string, gitReference string, schemaFile string) (string, error) { + if baseURL == "" { + baseURL = defaultECSSchemaBaseURL + } + parsedBaseURL, err := url.Parse(baseURL) + switch { + case err != nil: + return "", fmt.Errorf("invalid base URL (%s) for ECS schema: %w", baseURL, err) + case parsedBaseURL.Scheme != "http" && parsedBaseURL.Scheme != "https": + return "", fmt.Errorf("invalid scheme in base URL, found %s, expected http or https", parsedBaseURL.Scheme) + } + return parsedBaseURL.JoinPath(gitReference, "generated", "ecs", schemaFile).String(), nil +} + // InjectFieldsOptions allow to configure fields injection. type InjectFieldsOptions struct { // KeepExternal can be set to true to avoid deleting the `external` parameter diff --git a/internal/fields/dependency_manager_test.go b/internal/fields/dependency_manager_test.go index bf9c8b1f62..bf14d5198e 100644 --- a/internal/fields/dependency_manager_test.go +++ b/internal/fields/dependency_manager_test.go @@ -837,3 +837,62 @@ func TestValidate_SetExternalECS(t *testing.T) { }) } } + +func TestECSSchemaURL(t *testing.T) { + cases := []struct { + title string + baseURL string + gitReference string + schemaFile string + expected string + expectedErr bool + }{ + { + title: "default", + gitReference: "v8.11.0", + schemaFile: ecsSchemaFile, + expected: "https://raw.githubusercontent.com/elastic/ecs/v8.11.0/generated/ecs/ecs_nested.yml", + }, + { + title: "fork in github", + baseURL: "https://raw.githubusercontent.com/jsoriano/ecs", + gitReference: "v8.11.0", + schemaFile: ecsSchemaFile, + expected: "https://raw.githubusercontent.com/jsoriano/ecs/v8.11.0/generated/ecs/ecs_nested.yml", + }, + { + title: "fork in forgejo", + baseURL: "https://somehost.org/raw/jsoriano/ecs", + gitReference: "v8.11.0", + schemaFile: ecsSchemaFile, + expected: "https://somehost.org/raw/jsoriano/ecs/v8.11.0/generated/ecs/ecs_nested.yml", + }, + { + title: "invalid URL", + baseURL: "/somehost.org/raw/elastic/ecs", + gitReference: "v8.11.0", + schemaFile: ecsSchemaFile, + expectedErr: true, + }, + { + title: "invalid scheme", + baseURL: "file://../../..", + gitReference: "v8.11.0", + schemaFile: ecsSchemaFile, + expectedErr: true, + }, + } + + for _, c := range cases { + t.Run(c.title, func(t *testing.T) { + found, err := ecsSchemaURL(c.baseURL, c.gitReference, c.schemaFile) + t.Log(found) + if c.expectedErr { + assert.Error(t, err) + return + } + require.NoError(t, err) + assert.Equal(t, c.expected, found) + }) + } +} diff --git a/internal/fields/validate.go b/internal/fields/validate.go index 6bf0f11285..a2c6d07737 100644 --- a/internal/fields/validate.go +++ b/internal/fields/validate.go @@ -157,6 +157,8 @@ type Validator struct { enabledOTelValidation bool injectFieldsOptions InjectFieldsOptions + + schemaURLs SchemaURLs } // ValidatorOption represents an optional flag that can be passed to CreateValidatorForDirectory. @@ -256,6 +258,14 @@ func WithOTelValidation(otelValidation bool) ValidatorOption { } } +// WithSchemaURLs configures the validator to use custom sources for schemas. +func WithSchemaURLs(schemaURLs SchemaURLs) ValidatorOption { + return func(v *Validator) error { + v.schemaURLs = schemaURLs + return nil + } +} + type packageRootFinder interface { FindPackageRoot() (string, error) } @@ -298,7 +308,7 @@ func createValidatorForDirectoryAndPackageRoot(fieldsParentDir string, finder pa return nil, fmt.Errorf("can't find package root: %w", err) } - fdm, v.Schema, err = initDependencyManagement(packageRoot, v.specVersion, v.enabledImportAllECSSchema) + fdm, v.Schema, err = initDependencyManagement(packageRoot, v.specVersion, v.enabledImportAllECSSchema, v.schemaURLs) if err != nil { return nil, fmt.Errorf("failed to initialize dependency management: %w", err) } @@ -313,7 +323,7 @@ func createValidatorForDirectoryAndPackageRoot(fieldsParentDir string, finder pa return v, nil } -func initDependencyManagement(packageRoot string, specVersion semver.Version, importECSSchema bool) (*DependencyManager, []FieldDefinition, error) { +func initDependencyManagement(packageRoot string, specVersion semver.Version, importECSSchema bool, urls SchemaURLs) (*DependencyManager, []FieldDefinition, error) { buildManifest, ok, err := buildmanifest.ReadBuildManifest(packageRoot) if err != nil { return nil, nil, fmt.Errorf("can't read build manifest: %w", err) @@ -323,7 +333,7 @@ func initDependencyManagement(packageRoot string, specVersion semver.Version, im return nil, nil, nil } - fdm, err := CreateFieldDependencyManager(buildManifest.Dependencies) + fdm, err := CreateFieldDependencyManager(buildManifest.Dependencies, urls) if err != nil { return nil, nil, fmt.Errorf("can't create field dependency manager: %w", err) } diff --git a/internal/install/application_configuration.go b/internal/install/application_configuration.go index 41e35b1134..ae6376ff4b 100644 --- a/internal/install/application_configuration.go +++ b/internal/install/application_configuration.go @@ -17,6 +17,7 @@ import ( "github.com/elastic/elastic-package/internal/configuration/locations" "github.com/elastic/elastic-package/internal/environment" + "github.com/elastic/elastic-package/internal/fields" "github.com/elastic/elastic-package/internal/logger" "github.com/elastic/elastic-package/internal/profile" ) @@ -39,6 +40,8 @@ const ( logstashImageName = "docker.elastic.co/logstash/logstash" isReadyImageName = "tianon/true:multiarch" + defaultECSSchemaBaseURL = "https://raw.githubusercontent.com/elastic/ecs" + applicationConfigurationYmlFile = "config.yml" ) @@ -69,6 +72,8 @@ func DefaultConfiguration() *ApplicationConfiguration { // }, // } + config.c.SchemaURLs.ECSBase = defaultECSSchemaBaseURL + return &config } @@ -85,6 +90,7 @@ type configFile struct { Profile struct { Current string `yaml:"current"` } `yaml:"profile"` + SchemaURLs fields.SchemaURLs `yaml:"schema_urls"` } type stack struct { @@ -156,6 +162,10 @@ func (ac *ApplicationConfiguration) SetCurrentProfile(name string) { ac.c.Profile.Current = name } +func (ac *ApplicationConfiguration) SchemaURLs() fields.SchemaURLs { + return ac.c.SchemaURLs +} + // selectElasticAgentImageName function returns the appropriate image name for Elastic-Agent depending on the stack version. // This is mandatory as "elastic-agent-complete" is available since 7.15.0-SNAPSHOT. func selectElasticAgentImageName(agentVersion, agentBaseImage string) string { diff --git a/internal/packages/installer/factory.go b/internal/packages/installer/factory.go index 0b29c79034..baa61375d1 100644 --- a/internal/packages/installer/factory.go +++ b/internal/packages/installer/factory.go @@ -13,6 +13,7 @@ import ( "github.com/Masterminds/semver/v3" "github.com/elastic/elastic-package/internal/builder" + "github.com/elastic/elastic-package/internal/fields" "github.com/elastic/elastic-package/internal/kibana" "github.com/elastic/elastic-package/internal/logger" "github.com/elastic/elastic-package/internal/packages" @@ -39,6 +40,7 @@ type Options struct { ZipPath string SkipValidation bool RepositoryRoot *os.Root // Root of the repository where package source code is located. + SchemaURLs fields.SchemaURLs } // NewForPackage creates a new installer for a package, given its root path, or its prebuilt zip. @@ -92,6 +94,7 @@ func NewForPackage(options Options) (Installer, error) { SkipValidation: options.SkipValidation, RepositoryRoot: options.RepositoryRoot, UpdateReadmes: false, + SchemaURLs: options.SchemaURLs, }) if err != nil { return nil, fmt.Errorf("failed to build package: %v", err) From 3746c8f92755f9815ee16e46c92574b0256b0641 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Wed, 26 Nov 2025 18:16:47 +0100 Subject: [PATCH 2/2] More required changes --- cmd/lint.go | 8 +++++- internal/builder/packages.go | 2 +- internal/docs/exported_fields.go | 7 +++-- internal/docs/readme.go | 27 ++++++++++--------- internal/fields/dependency_manager.go | 4 +-- internal/fields/dependency_manager_test.go | 13 ++++++++- internal/install/application_configuration.go | 1 + 7 files changed, 41 insertions(+), 21 deletions(-) diff --git a/cmd/lint.go b/cmd/lint.go index eaa2c922e7..3ba82f572f 100644 --- a/cmd/lint.go +++ b/cmd/lint.go @@ -12,6 +12,7 @@ import ( "github.com/elastic/elastic-package/internal/cobraext" "github.com/elastic/elastic-package/internal/docs" "github.com/elastic/elastic-package/internal/files" + "github.com/elastic/elastic-package/internal/install" "github.com/elastic/elastic-package/internal/logger" "github.com/elastic/elastic-package/internal/packages" "github.com/elastic/elastic-package/internal/validation" @@ -57,7 +58,12 @@ func lintCommandAction(cmd *cobra.Command, args []string) error { return fmt.Errorf("package root not found: %w", err) } - readmeFiles, err := docs.AreReadmesUpToDate(repositoryRoot, packageRoot) + config, err := install.Configuration() + if err != nil { + return fmt.Errorf("can't load configuration: %w", err) + } + + readmeFiles, err := docs.AreReadmesUpToDate(repositoryRoot, packageRoot, config.SchemaURLs()) if err != nil { for _, f := range readmeFiles { if !f.UpToDate { diff --git a/internal/builder/packages.go b/internal/builder/packages.go index 711c595384..2c68e6b581 100644 --- a/internal/builder/packages.go +++ b/internal/builder/packages.go @@ -233,7 +233,7 @@ func BuildPackage(options BuildOptions) (string, error) { } if options.UpdateReadmes { - err = docs.UpdateReadmes(options.RepositoryRoot, options.PackageRoot, buildPackageRoot) + err = docs.UpdateReadmes(options.RepositoryRoot, options.PackageRoot, buildPackageRoot, options.SchemaURLs) if err != nil { return "", fmt.Errorf("updating readme files failed: %w", err) } diff --git a/internal/docs/exported_fields.go b/internal/docs/exported_fields.go index 4f2896d2e3..c171b58d5a 100644 --- a/internal/docs/exported_fields.go +++ b/internal/docs/exported_fields.go @@ -25,7 +25,7 @@ var escaper = strings.NewReplacer("*", "\\*", "{", "\\{", "}", "\\}", "<", "\\<" // renderExportedFields renders the fields for a package or data stream, fieldsParentRoot must be // the path to the root directory of the package or data stream. -func renderExportedFields(fieldsParentRoot string) (string, error) { +func renderExportedFields(fieldsParentRoot string, schemaURLs fields.SchemaURLs) (string, error) { injectOptions := fields.InjectFieldsOptions{ // Keep External parameter when rendering fields, so we can render // documentation for empty groups imported from ECS, for backwards compatibility. @@ -35,7 +35,10 @@ func renderExportedFields(fieldsParentRoot string) (string, error) { // keep them to accept them for validation. SkipEmptyFields: true, } - validator, err := fields.CreateValidatorForDirectory(fieldsParentRoot, fields.WithInjectFieldsOptions(injectOptions)) + validator, err := fields.CreateValidatorForDirectory(fieldsParentRoot, + fields.WithInjectFieldsOptions(injectOptions), + fields.WithSchemaURLs(schemaURLs), + ) if err != nil { return "", fmt.Errorf("can't create fields validator instance (path: %s): %w", fieldsParentRoot, err) } diff --git a/internal/docs/readme.go b/internal/docs/readme.go index 5ea9bf976c..ea47d556be 100644 --- a/internal/docs/readme.go +++ b/internal/docs/readme.go @@ -14,6 +14,7 @@ import ( "github.com/pmezard/go-difflib/difflib" + "github.com/elastic/elastic-package/internal/fields" "github.com/elastic/elastic-package/internal/logger" ) @@ -31,7 +32,7 @@ const ( ) // AreReadmesUpToDate function checks if all the .md readme files are up-to-date. -func AreReadmesUpToDate(repositoryRoot *os.Root, packageRoot string) ([]ReadmeFile, error) { +func AreReadmesUpToDate(repositoryRoot *os.Root, packageRoot string, schemaURLs fields.SchemaURLs) ([]ReadmeFile, error) { linksFilePath, err := linksDefinitionsFilePath(repositoryRoot) if err != nil { return nil, fmt.Errorf("locating links file failed: %w", err) @@ -45,7 +46,7 @@ func AreReadmesUpToDate(repositoryRoot *os.Root, packageRoot string) ([]ReadmeFi var readmeFiles []ReadmeFile for _, filePath := range files { fileName := filepath.Base(filePath) - ok, diff, err := isReadmeUpToDate(fileName, linksFilePath, packageRoot) + ok, diff, err := isReadmeUpToDate(fileName, linksFilePath, packageRoot, schemaURLs) if !ok || err != nil { readmeFile := ReadmeFile{ FileName: fileName, @@ -64,11 +65,11 @@ func AreReadmesUpToDate(repositoryRoot *os.Root, packageRoot string) ([]ReadmeFi } // isReadmeUpToDate function checks if a single readme file is up-to-date. -func isReadmeUpToDate(fileName, linksFilePath, packageRoot string) (bool, string, error) { +func isReadmeUpToDate(fileName, linksFilePath, packageRoot string, schemaURLs fields.SchemaURLs) (bool, string, error) { logger.Debugf("Check if %s is up-to-date", fileName) // the readme is generated within the package root, so source should be the packageRoot files too - rendered, shouldBeRendered, err := generateReadme(fileName, linksFilePath, packageRoot) + rendered, shouldBeRendered, err := generateReadme(fileName, linksFilePath, packageRoot, schemaURLs) if err != nil { return false, "", fmt.Errorf("generating readme file failed: %w", err) } @@ -99,7 +100,7 @@ func isReadmeUpToDate(fileName, linksFilePath, packageRoot string) (bool, string // UpdateReadmes function updates all .md readme files using a defined template // files. The function doesn't perform any action if the template file is not present. -func UpdateReadmes(repositoryRoot *os.Root, packageRoot, buildPackageRoot string) error { +func UpdateReadmes(repositoryRoot *os.Root, packageRoot, buildPackageRoot string, schemaURLs fields.SchemaURLs) error { linksFilePath, err := linksDefinitionsFilePath(repositoryRoot) if err != nil { return fmt.Errorf("locating links file failed: %w", err) @@ -112,7 +113,7 @@ func UpdateReadmes(repositoryRoot *os.Root, packageRoot, buildPackageRoot string for _, filePath := range readmeFiles { fileName := filepath.Base(filePath) - target, err := updateReadme(fileName, linksFilePath, packageRoot, buildPackageRoot) + target, err := updateReadme(fileName, linksFilePath, packageRoot, buildPackageRoot, schemaURLs) if err != nil { return fmt.Errorf("updating readme file %s failed: %w", fileName, err) } @@ -128,10 +129,10 @@ func UpdateReadmes(repositoryRoot *os.Root, packageRoot, buildPackageRoot string // updateReadme function updates a single readme file using a defined template file. // It writes the rendered file to both the package directory and the package build directory. -func updateReadme(fileName, linksFilePath, packageRoot, buildPackageRoot string) (string, error) { +func updateReadme(fileName, linksFilePath, packageRoot, buildPackageRoot string, schemaURLs fields.SchemaURLs) (string, error) { logger.Debugf("Update the %s file", fileName) - rendered, shouldBeRendered, err := generateReadme(fileName, linksFilePath, packageRoot) + rendered, shouldBeRendered, err := generateReadme(fileName, linksFilePath, packageRoot, schemaURLs) if err != nil { return "", err } @@ -154,7 +155,7 @@ func updateReadme(fileName, linksFilePath, packageRoot, buildPackageRoot string) // generateReadme function generates the readme file content // the readme takes a template that lives under the _dev/build/docs directory at the packageRoot. // the readme template reads data from the packageRoot directory. -func generateReadme(fileName, linksFilePath, packageRoot string) ([]byte, bool, error) { +func generateReadme(fileName, linksFilePath, packageRoot string, schemaURLs fields.SchemaURLs) ([]byte, bool, error) { logger.Debugf("Generate %s file (package: %s)", fileName, packageRoot) templatePath, found, err := findReadmeTemplatePath(fileName, packageRoot) if err != nil { @@ -173,7 +174,7 @@ func generateReadme(fileName, linksFilePath, packageRoot string) ([]byte, bool, // templatePath lives under the _dev/build/docs directory at the package root. // builtPackageRoot is the root directory of the built package. - rendered, err := renderReadme(fileName, packageRoot, templatePath, linksMap) + rendered, err := renderReadme(fileName, packageRoot, templatePath, linksMap, schemaURLs) if err != nil { return nil, true, fmt.Errorf("rendering Readme failed: %w", err) } @@ -194,7 +195,7 @@ func findReadmeTemplatePath(fileName, packageRoot string) (string, bool, error) } // renderReadme function renders the readme file reading from -func renderReadme(fileName, packageRoot, templatePath string, linksMap linkMap) ([]byte, error) { +func renderReadme(fileName, packageRoot, templatePath string, linksMap linkMap, schemaURLs fields.SchemaURLs) ([]byte, error) { logger.Debugf("Render %s file (package: %s, templatePath: %s)", fileName, packageRoot, templatePath) t := template.New(fileName) @@ -208,9 +209,9 @@ func renderReadme(fileName, packageRoot, templatePath string, linksMap linkMap) "fields": func(args ...string) (string, error) { if len(args) > 0 { dataStreamRoot := filepath.Join(packageRoot, "data_stream", args[0]) - return renderExportedFields(dataStreamRoot) + return renderExportedFields(dataStreamRoot, schemaURLs) } - return renderExportedFields(packageRoot) + return renderExportedFields(packageRoot, schemaURLs) }, "url": func(args ...string) (string, error) { options := linkOptions{} diff --git a/internal/fields/dependency_manager.go b/internal/fields/dependency_manager.go index c54e252b70..385dc0ef12 100644 --- a/internal/fields/dependency_manager.go +++ b/internal/fields/dependency_manager.go @@ -173,11 +173,9 @@ func asGitReference(reference string) (string, error) { return reference[len(gitReferencePrefix):], nil } -const defaultECSSchemaBaseURL = "https://raw.githubusercontent.com/elastic/ecs" - func ecsSchemaURL(baseURL string, gitReference string, schemaFile string) (string, error) { if baseURL == "" { - baseURL = defaultECSSchemaBaseURL + return "", errors.New("no base URL configured") } parsedBaseURL, err := url.Parse(baseURL) switch { diff --git a/internal/fields/dependency_manager_test.go b/internal/fields/dependency_manager_test.go index bf14d5198e..b7ab45d1b4 100644 --- a/internal/fields/dependency_manager_test.go +++ b/internal/fields/dependency_manager_test.go @@ -621,7 +621,10 @@ func TestDependencyManagerWithECS(t *testing.T) { Reference: "file://" + ecsNestedPath8_10_0, }, } - dm, err := CreateFieldDependencyManager(deps) + urls := SchemaURLs{ + ECSBase: "https://raw.githubusercontent.com/elastic/ecs", + } + dm, err := CreateFieldDependencyManager(deps, urls) require.NoError(t, err) cases := []struct { @@ -849,6 +852,7 @@ func TestECSSchemaURL(t *testing.T) { }{ { title: "default", + baseURL: "https://raw.githubusercontent.com/elastic/ecs", gitReference: "v8.11.0", schemaFile: ecsSchemaFile, expected: "https://raw.githubusercontent.com/elastic/ecs/v8.11.0/generated/ecs/ecs_nested.yml", @@ -881,6 +885,13 @@ func TestECSSchemaURL(t *testing.T) { schemaFile: ecsSchemaFile, expectedErr: true, }, + { + title: "no URL", + baseURL: "", + gitReference: "v8.11.0", + schemaFile: ecsSchemaFile, + expectedErr: true, + }, } for _, c := range cases { diff --git a/internal/install/application_configuration.go b/internal/install/application_configuration.go index ae6376ff4b..9d0ac9cb1a 100644 --- a/internal/install/application_configuration.go +++ b/internal/install/application_configuration.go @@ -162,6 +162,7 @@ func (ac *ApplicationConfiguration) SetCurrentProfile(name string) { ac.c.Profile.Current = name } +// SchemaURLs returns the URLs used to retrieve schemas. func (ac *ApplicationConfiguration) SchemaURLs() fields.SchemaURLs { return ac.c.SchemaURLs }