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 }