blob: 85442f3bf7e58ed6ebc2282cfb6fc5b62fd76855 [file] [log] [blame]
// 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
}