github.com/argoproj/argo-cd/v2@v2.10.9/hack/gen-catalog/main.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"path"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"github.com/spf13/cobra/doc"
    13  
    14  	"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/admin"
    15  
    16  	"github.com/argoproj/notifications-engine/pkg/services"
    17  	"github.com/argoproj/notifications-engine/pkg/triggers"
    18  	"github.com/argoproj/notifications-engine/pkg/util/misc"
    19  	"github.com/olekukonko/tablewriter"
    20  	log "github.com/sirupsen/logrus"
    21  	"github.com/spf13/cobra"
    22  	v1 "k8s.io/api/core/v1"
    23  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    24  	"sigs.k8s.io/yaml"
    25  )
    26  
    27  func main() {
    28  	var command = &cobra.Command{
    29  		Use: "gen",
    30  		Run: func(c *cobra.Command, args []string) {
    31  			c.HelpFunc()(c, args)
    32  		},
    33  	}
    34  	command.AddCommand(newDocsCommand())
    35  	command.AddCommand(newCatalogCommand())
    36  
    37  	if err := command.Execute(); err != nil {
    38  		fmt.Println(err)
    39  		os.Exit(1)
    40  	}
    41  }
    42  
    43  func newCatalogCommand() *cobra.Command {
    44  	return &cobra.Command{
    45  		Use: "catalog",
    46  		Run: func(c *cobra.Command, args []string) {
    47  			cm := v1.ConfigMap{
    48  				TypeMeta: metav1.TypeMeta{
    49  					Kind:       "ConfigMap",
    50  					APIVersion: "v1",
    51  				},
    52  				ObjectMeta: metav1.ObjectMeta{
    53  					Name: "argocd-notifications-cm",
    54  				},
    55  				Data: make(map[string]string),
    56  			}
    57  			wd, err := os.Getwd()
    58  			dieOnError(err, "Failed to get current working directory")
    59  			target := path.Join(wd, "notifications_catalog/install.yaml")
    60  
    61  			templatesDir := path.Join(wd, "notifications_catalog/templates")
    62  			triggersDir := path.Join(wd, "notifications_catalog/triggers")
    63  
    64  			templates, triggers, err := buildConfigFromFS(templatesDir, triggersDir)
    65  			dieOnError(err, "Failed to build catalog config")
    66  
    67  			misc.IterateStringKeyMap(triggers, func(name string) {
    68  				trigger := triggers[name]
    69  				t, err := yaml.Marshal(trigger)
    70  				dieOnError(err, "Failed to marshal trigger")
    71  				cm.Data[fmt.Sprintf("trigger.%s", name)] = string(t)
    72  			})
    73  
    74  			misc.IterateStringKeyMap(templates, func(name string) {
    75  				template := templates[name]
    76  				t, err := yaml.Marshal(template)
    77  				dieOnError(err, "Failed to marshal template")
    78  				cm.Data[fmt.Sprintf("template.%s", name)] = string(t)
    79  			})
    80  
    81  			d, err := yaml.Marshal(cm)
    82  			dieOnError(err, "Failed to marshal final configmap")
    83  
    84  			err = os.WriteFile(target, d, 0644)
    85  			dieOnError(err, "Failed to write builtin configmap")
    86  
    87  		},
    88  	}
    89  }
    90  
    91  func newDocsCommand() *cobra.Command {
    92  	return &cobra.Command{
    93  		Use: "docs",
    94  		Run: func(c *cobra.Command, args []string) {
    95  			var builtItDocsData bytes.Buffer
    96  			wd, err := os.Getwd()
    97  			dieOnError(err, "Failed to get current working directory")
    98  
    99  			templatesDir := path.Join(wd, "notifications_catalog/templates")
   100  			triggersDir := path.Join(wd, "notifications_catalog/triggers")
   101  
   102  			notificationTemplates, notificationTriggers, err := buildConfigFromFS(templatesDir, triggersDir)
   103  			dieOnError(err, "Failed to build builtin config")
   104  			generateBuiltInTriggersDocs(&builtItDocsData, notificationTriggers, notificationTemplates)
   105  			if err := os.WriteFile("./docs/operator-manual/notifications/catalog.md", builtItDocsData.Bytes(), 0644); err != nil {
   106  				log.Fatal(err)
   107  			}
   108  			var commandDocs bytes.Buffer
   109  			if err := generateCommandsDocs(&commandDocs); err != nil {
   110  				log.Fatal(err)
   111  			}
   112  			if err := os.WriteFile("./docs/operator-manual/notifications/troubleshooting-commands.md", commandDocs.Bytes(), 0644); err != nil {
   113  				log.Fatal(err)
   114  			}
   115  		},
   116  	}
   117  }
   118  
   119  func generateBuiltInTriggersDocs(out io.Writer, triggers map[string][]triggers.Condition, templates map[string]services.Notification) {
   120  	_, _ = fmt.Fprintln(out, "# Triggers and Templates Catalog")
   121  	_, _ = fmt.Fprintln(out, "## Triggers")
   122  
   123  	w := tablewriter.NewWriter(out)
   124  	w.SetHeader([]string{"NAME", "DESCRIPTION", "TEMPLATE"})
   125  	w.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
   126  	w.SetCenterSeparator("|")
   127  	w.SetAutoWrapText(false)
   128  	misc.IterateStringKeyMap(triggers, func(name string) {
   129  		t := triggers[name]
   130  		desc := ""
   131  		template := ""
   132  		if len(t) > 0 {
   133  			desc = t[0].Description
   134  			template = strings.Join(t[0].Send, ",")
   135  		}
   136  		w.Append([]string{name, desc, fmt.Sprintf("[%s](#%s)", template, template)})
   137  	})
   138  	w.Render()
   139  
   140  	_, _ = fmt.Fprintln(out, "")
   141  	_, _ = fmt.Fprintln(out, "## Templates")
   142  	misc.IterateStringKeyMap(templates, func(name string) {
   143  		t := templates[name]
   144  		yamlData, err := yaml.Marshal(t)
   145  		if err != nil {
   146  			panic(err)
   147  		}
   148  		_, _ = fmt.Fprintf(out, "### %s\n**definition**:\n```yaml\n%s\n```\n", name, string(yamlData))
   149  	})
   150  }
   151  
   152  func generateCommandsDocs(out io.Writer) error {
   153  	// create parents so that CommandPath() is correctly resolved
   154  	mainCmd := &cobra.Command{Use: "argocd"}
   155  	adminCmd := &cobra.Command{Use: "admin"}
   156  	toolCmd := admin.NewNotificationsCommand()
   157  	adminCmd.AddCommand(toolCmd)
   158  	mainCmd.AddCommand(adminCmd)
   159  	for _, mainSubCommand := range mainCmd.Commands() {
   160  		for _, adminSubCommand := range mainSubCommand.Commands() {
   161  			for _, toolSubCommand := range adminSubCommand.Commands() {
   162  				for _, c := range toolSubCommand.Commands() {
   163  					var cmdDesc bytes.Buffer
   164  					if err := doc.GenMarkdown(c, &cmdDesc); err != nil {
   165  						return err
   166  					}
   167  					for _, line := range strings.Split(cmdDesc.String(), "\n") {
   168  						if strings.HasPrefix(line, "### SEE ALSO") {
   169  							break
   170  						}
   171  						_, _ = fmt.Fprintf(out, "%s\n", line)
   172  					}
   173  				}
   174  			}
   175  		}
   176  	}
   177  	return nil
   178  }
   179  
   180  func dieOnError(err error, msg string) {
   181  	if err != nil {
   182  		fmt.Printf("[ERROR] %s: %v", msg, err)
   183  		os.Exit(1)
   184  	}
   185  }
   186  
   187  func buildConfigFromFS(templatesDir string, triggersDir string) (map[string]services.Notification, map[string][]triggers.Condition, error) {
   188  	templatesCfg := map[string]services.Notification{}
   189  	err := filepath.Walk(templatesDir, func(p string, info os.FileInfo, e error) error {
   190  		if e != nil {
   191  			return e
   192  		}
   193  		if info.IsDir() {
   194  			return nil
   195  		}
   196  		data, err := os.ReadFile(p)
   197  		if err != nil {
   198  			return err
   199  		}
   200  		name := strings.Split(path.Base(p), ".")[0]
   201  		var template services.Notification
   202  		if err := yaml.Unmarshal(data, &template); err != nil {
   203  			return err
   204  		}
   205  		templatesCfg[name] = template
   206  		return nil
   207  	})
   208  	if err != nil {
   209  		return nil, nil, err
   210  	}
   211  
   212  	triggersCfg := map[string][]triggers.Condition{}
   213  	err = filepath.Walk(triggersDir, func(p string, info os.FileInfo, e error) error {
   214  		if e != nil {
   215  			return e
   216  		}
   217  		if info.IsDir() {
   218  			return nil
   219  		}
   220  		data, err := os.ReadFile(p)
   221  		if err != nil {
   222  			return err
   223  		}
   224  		name := strings.Split(path.Base(p), ".")[0]
   225  		var trigger []triggers.Condition
   226  		if err := yaml.Unmarshal(data, &trigger); err != nil {
   227  			return err
   228  		}
   229  		triggersCfg[name] = trigger
   230  		return nil
   231  	})
   232  	if err != nil {
   233  		return nil, nil, err
   234  	}
   235  	return templatesCfg, triggersCfg, nil
   236  }