| // Licensed to Apache Software Foundation (ASF) under one or more contributor |
| // license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright |
| // ownership. Apache Software Foundation (ASF) 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 tools |
| |
| import ( |
| "fmt" |
| "io/ioutil" |
| "os" |
| "reflect" |
| "sort" |
| "strings" |
| |
| fetcher_api "github.com/apache/skywalking-satellite/plugins/fetcher/api" |
| forwarder_api "github.com/apache/skywalking-satellite/plugins/forwarder/api" |
| receiver_api "github.com/apache/skywalking-satellite/plugins/receiver/api" |
| |
| "github.com/apache/skywalking-satellite/internal/pkg/log" |
| "github.com/apache/skywalking-satellite/internal/pkg/plugin" |
| "github.com/apache/skywalking-satellite/plugins" |
| ) |
| |
| const ( |
| topLevel = "# " |
| secondLevel = "## " |
| lf = "\n" |
| yamlQuoteStart = "```yaml" |
| yamlQuoteEnd = "```" |
| markdownSuffix = ".md" |
| ) |
| |
| func GeneratePluginDoc(outputRootPath, menuFilePath, pluginFilePath string) error { |
| log.Init(&log.LoggerConfig{}) |
| plugins.RegisterPlugins() |
| |
| pluginPath := fmt.Sprintf("%s%s", outputRootPath, pluginFilePath) |
| if err := createDir(pluginPath); err != nil { |
| return fmt.Errorf("create docs dir error: %v", err) |
| } |
| if err := generatePluginListDoc(pluginPath, getSortedCategories()); err != nil { |
| return err |
| } |
| if err := updateMenuPluginListDoc(outputRootPath, menuFilePath, pluginFilePath, getSortedCategories()); err != nil { |
| return err |
| } |
| log.Logger.Info("Successfully generate documentation!") |
| return nil |
| } |
| |
| // sort categories by dictionary sequence |
| func getSortedCategories() []reflect.Type { |
| var categories []reflect.Type |
| for c := range plugin.Reg { |
| categories = append(categories, c) |
| } |
| sort.Slice(categories, func(i, j int) bool { |
| return strings.Compare(categories[i].String(), categories[j].String()) <= 0 |
| }) |
| return categories |
| } |
| |
| func updateMenuPluginListDoc(outputRootPath, menuFilePath, pluginFilePath string, categories []reflect.Type) error { |
| menuFile := fmt.Sprintf("%s%s", outputRootPath, menuFilePath) |
| menu, err := LoadCatalog(menuFile) |
| if err != nil { |
| return err |
| } |
| |
| // find plugin Catalog |
| pluginCatalog := menu.Find("Setup", "Plugins") |
| if pluginCatalog == nil { |
| return fmt.Errorf("cannot find plugins Catalog") |
| } |
| |
| // remove path |
| pluginCatalog.Path = "" |
| |
| // rebuild all plugins |
| var allPlugins []*Catalog |
| for _, category := range categories { |
| // plugin |
| implements := []*Catalog{} |
| curPlugin := &Catalog{ |
| Name: strings.ToLower(category.Name()), |
| } |
| |
| // all implements |
| pluginList := getPluginsByCategory(category) |
| for _, pluginName := range pluginList { |
| implements = append(implements, &Catalog{ |
| Name: pluginName, |
| Path: strings.TrimRight(fmt.Sprintf("%s/%s", pluginFilePath, getPluginDocFileName(category, pluginName)), markdownSuffix), |
| }) |
| } |
| curPlugin.Catalog = implements |
| |
| if len(implements) > 0 { |
| allPlugins = append(allPlugins, curPlugin) |
| } |
| } |
| pluginCatalog.Catalog = allPlugins |
| |
| return menu.Save(menuFile) |
| } |
| |
| func generatePluginListDoc(docDir string, categories []reflect.Type) error { |
| fileName := docDir + "/" + "plugin-list" + markdownSuffix |
| doc := topLevel + "Plugin List" + lf |
| for _, category := range categories { |
| doc += "- " + category.Name() + lf |
| pluginList := getPluginsByCategory(category) |
| for _, pluginName := range pluginList { |
| doc += " - [" + pluginName + "](./" + getPluginDocFileName(category, pluginName) + ")" + lf |
| if err := generatePluginDoc(docDir, category, pluginName); err != nil { |
| return err |
| } |
| } |
| } |
| return writeDoc([]byte(doc), fileName) |
| } |
| |
| func generatePluginDoc(docDir string, category reflect.Type, pluginName string) error { |
| docFileName := docDir + "/" + getPluginDocFileName(category, pluginName) |
| p := plugin.Get(category, plugin.Config{plugin.NameField: pluginName}) |
| doc := topLevel + category.Name() + "/" + pluginName + lf |
| doc += secondLevel + "Description" + lf |
| doc += p.Description() + lf |
| doc += generateSupportForwarders(category, p) |
| doc += secondLevel + "DefaultConfig" + lf |
| doc += yamlQuoteStart + p.DefaultConfig() + yamlQuoteEnd + lf |
| return writeDoc([]byte(doc), docFileName) |
| } |
| |
| func generateSupportForwarders(category reflect.Type, p plugin.Plugin) string { |
| var forwarders []forwarder_api.Forwarder |
| if category.Name() == "Receiver" { |
| forwarders = p.(receiver_api.Receiver).SupportForwarders() |
| } else if category.Name() == "Fetcher" { |
| forwarders = p.(fetcher_api.Fetcher).SupportForwarders() |
| } |
| if len(forwarders) == 0 { |
| return "" |
| } |
| result := secondLevel + "Support Forwarders" + lf |
| for _, forwarder := range forwarders { |
| result += " - [" + forwarder.Name() + "](" + getPluginDocFileName(reflect.TypeOf(forwarder).Elem(), forwarder.Name()) + ")" + lf |
| } |
| return result |
| } |
| |
| func getPluginsByCategory(category reflect.Type) []string { |
| mapping := plugin.Reg[category] |
| var keys []string |
| for k := range mapping { |
| keys = append(keys, k) |
| } |
| sort.Strings(keys) |
| return keys |
| } |
| |
| func getPluginDocFileName(category reflect.Type, pluginName string) string { |
| return strings.ToLower(category.Name() + "_" + pluginName + markdownSuffix) |
| } |
| |
| func writeDoc(doc []byte, docFileName string) error { |
| if err := ioutil.WriteFile(docFileName, doc, os.ModePerm); err != nil { |
| return fmt.Errorf("cannot init the plugin doc: %v", err) |
| } |
| return nil |
| } |
| |
| func createDir(path string) error { |
| err := os.RemoveAll(path) |
| if err != nil { |
| return err |
| } |
| fileInfo, err := os.Stat(path) |
| if os.IsNotExist(err) || fileInfo.Size() == 0 { |
| return os.Mkdir(path, os.ModePerm) |
| } |
| return err |
| } |