// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package mage import ( "io/ioutil" "os" "path/filepath" "sort" "strings" "github.com/pkg/errors" "gopkg.in/yaml.v2" ) const moduleConfigTemplate = ` #========================== Modules configuration ============================= {{.BeatName}}.modules: {{range $mod := .Modules}} #{{$mod.Dashes}} {{$mod.Title | title}} Module {{$mod.Dashes}} {{$mod.Config}} {{- end}} ` type moduleConfigTemplateData struct { ID string Title string Dashes string Config string } type moduleFieldsYmlData []struct { Title string `json:"title"` ShortConfig bool `json:"short_config"` } func readModuleFieldsYml(path string) (title string, useShort bool, err error) { data, err := ioutil.ReadFile(path) if err != nil { return "", false, err } var fd moduleFieldsYmlData if err = yaml.Unmarshal(data, &fd); err != nil { return "", false, err } if len(fd) == 0 { return "", false, errors.New("module not found in fields.yml") } return fd[0].Title, fd[0].ShortConfig, nil } // moduleDashes returns a string containing the correct number of dashes '-' to // center the modules title in the middle of the line surrounded by an equal // number of dashes on each side. func moduleDashes(name string) string { const ( lineLen = 80 headerLen = len("#") titleSuffixLen = len(" Module ") ) numDashes := lineLen - headerLen - titleSuffixLen - len(name) numDashes /= 2 return strings.Repeat("-", numDashes) } // GenerateModuleReferenceConfig generates a reference config file and includes // modules found from the given module dirs. func GenerateModuleReferenceConfig(out string, moduleDirs ...string) error { var moduleConfigs []moduleConfigTemplateData for _, dir := range moduleDirs { modules, err := ioutil.ReadDir(dir) if err != nil { return err } for _, modDirInfo := range modules { if !modDirInfo.IsDir() { continue } name := modDirInfo.Name() // Get title from fields.yml. title, _, err := readModuleFieldsYml(filepath.Join(dir, name, "_meta/fields.yml")) if err != nil { title = strings.Title(name) } // Prioritize config.reference.yml, but fallback to config.yml. files := []string{ filepath.Join(dir, name, "_meta/config.reference.yml"), filepath.Join(dir, name, "_meta/config.yml"), } var data []byte for _, f := range files { data, err = ioutil.ReadFile(f) if err != nil { if os.IsNotExist(err) { continue } return err } break } if data == nil { continue } moduleConfigs = append(moduleConfigs, moduleConfigTemplateData{ ID: name, Title: title, Dashes: moduleDashes(title), Config: string(data), }) } } // Sort them by their module dir name, but put system first. sort.Slice(moduleConfigs, func(i, j int) bool { // Bubble system to the top of the list. if moduleConfigs[i].ID == "system" { return true } else if moduleConfigs[j].ID == "system" { return false } return moduleConfigs[i].ID < moduleConfigs[j].ID }) config := MustExpand(moduleConfigTemplate, map[string]interface{}{ "Modules": moduleConfigs, }) return ioutil.WriteFile(out, []byte(config), 0644) }