github.com/argoproj/argo-cd/v3@v3.2.1/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/argoproj/notifications-engine/pkg/services" 13 "github.com/argoproj/notifications-engine/pkg/triggers" 14 "github.com/argoproj/notifications-engine/pkg/util/misc" 15 "github.com/olekukonko/tablewriter" 16 "github.com/olekukonko/tablewriter/renderer" 17 "github.com/olekukonko/tablewriter/tw" 18 log "github.com/sirupsen/logrus" 19 "github.com/spf13/cobra" 20 "github.com/spf13/cobra/doc" 21 corev1 "k8s.io/api/core/v1" 22 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 "sigs.k8s.io/yaml" 24 25 "github.com/argoproj/argo-cd/v3/cmd/argocd/commands/admin" 26 ) 27 28 func main() { 29 command := &cobra.Command{ 30 Use: "gen", 31 Run: func(c *cobra.Command, args []string) { 32 c.HelpFunc()(c, args) 33 }, 34 } 35 command.AddCommand(newDocsCommand()) 36 command.AddCommand(newCatalogCommand()) 37 38 if err := command.Execute(); err != nil { 39 fmt.Println(err) 40 os.Exit(1) 41 } 42 } 43 44 func newCatalogCommand() *cobra.Command { 45 return &cobra.Command{ 46 Use: "catalog", 47 Run: func(_ *cobra.Command, _ []string) { 48 cm := corev1.ConfigMap{ 49 TypeMeta: metav1.TypeMeta{ 50 Kind: "ConfigMap", 51 APIVersion: "v1", 52 }, 53 ObjectMeta: metav1.ObjectMeta{ 54 Name: "argocd-notifications-cm", 55 }, 56 Data: make(map[string]string), 57 } 58 wd, err := os.Getwd() 59 dieOnError(err, "Failed to get current working directory") 60 target := path.Join(wd, "notifications_catalog/install.yaml") 61 62 templatesDir := path.Join(wd, "notifications_catalog/templates") 63 triggersDir := path.Join(wd, "notifications_catalog/triggers") 64 65 templates, triggers, err := buildConfigFromFS(templatesDir, triggersDir) 66 dieOnError(err, "Failed to build catalog config") 67 68 misc.IterateStringKeyMap(triggers, func(name string) { 69 trigger := triggers[name] 70 t, err := yaml.Marshal(trigger) 71 dieOnError(err, "Failed to marshal trigger") 72 cm.Data["trigger."+name] = string(t) 73 }) 74 75 misc.IterateStringKeyMap(templates, func(name string) { 76 template := templates[name] 77 t, err := yaml.Marshal(template) 78 dieOnError(err, "Failed to marshal template") 79 cm.Data["template."+name] = string(t) 80 }) 81 82 d, err := yaml.Marshal(cm) 83 dieOnError(err, "Failed to marshal final configmap") 84 85 err = os.WriteFile(target, d, 0o644) 86 dieOnError(err, "Failed to write builtin configmap") 87 }, 88 } 89 } 90 91 func newDocsCommand() *cobra.Command { 92 return &cobra.Command{ 93 Use: "docs", 94 Run: func(_ *cobra.Command, _ []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(), 0o644); 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(), 0o644); 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 122 _, _ = fmt.Fprintln(out, "## Getting Started") 123 _, _ = fmt.Fprintln(out, "* Install Triggers and Templates from the catalog") 124 _, _ = fmt.Fprintln(out, " ```bash") 125 _, _ = fmt.Fprintln(out, " kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/notifications_catalog/install.yaml") 126 _, _ = fmt.Fprintln(out, " ```") 127 128 _, _ = fmt.Fprintln(out, "## Triggers") 129 130 r := tw.Rendition{ 131 Borders: tw.Border{Left: tw.On, Top: tw.Off, Right: tw.On, Bottom: tw.Off}, 132 Symbols: tw.NewSymbolCustom("pipe-center").WithCenter("|").WithMidLeft("|").WithMidRight("|"), 133 } 134 c := tablewriter.NewConfigBuilder().WithRowAutoWrap(tw.WrapNone).Build() 135 table := tablewriter.NewTable(out, tablewriter.WithConfig(c), tablewriter.WithRenderer(renderer.NewBlueprint(r))) 136 table.Header("NAME", "DESCRIPTION", "TEMPLATE") 137 misc.IterateStringKeyMap(triggers, func(name string) { 138 t := triggers[name] 139 desc := "" 140 template := "" 141 if len(t) > 0 { 142 desc = t[0].Description 143 template = strings.Join(t[0].Send, ",") 144 } 145 err := table.Append([]string{name, desc, fmt.Sprintf("[%s](#%s)", template, template)}) 146 if err != nil { 147 panic(err) 148 } 149 }) 150 err := table.Render() 151 if err != nil { 152 panic(err) 153 } 154 155 _, _ = fmt.Fprintln(out, "") 156 _, _ = fmt.Fprintln(out, "## Templates") 157 misc.IterateStringKeyMap(templates, func(name string) { 158 t := templates[name] 159 yamlData, err := yaml.Marshal(t) 160 if err != nil { 161 panic(err) 162 } 163 _, _ = fmt.Fprintf(out, "### %s\n**definition**:\n```yaml\n%s\n```\n", name, string(yamlData)) 164 }) 165 } 166 167 func generateCommandsDocs(out io.Writer) error { 168 // create parents so that CommandPath() is correctly resolved 169 mainCmd := &cobra.Command{Use: "argocd"} 170 adminCmd := &cobra.Command{Use: "admin"} 171 toolCmd := admin.NewNotificationsCommand() 172 adminCmd.AddCommand(toolCmd) 173 mainCmd.AddCommand(adminCmd) 174 for _, mainSubCommand := range mainCmd.Commands() { 175 for _, adminSubCommand := range mainSubCommand.Commands() { 176 for _, toolSubCommand := range adminSubCommand.Commands() { 177 for _, c := range toolSubCommand.Commands() { 178 var cmdDesc bytes.Buffer 179 if err := doc.GenMarkdown(c, &cmdDesc); err != nil { 180 return fmt.Errorf("error generating Markdown for command: %v : %w", c, err) 181 } 182 for _, line := range strings.Split(cmdDesc.String(), "\n") { 183 if strings.HasPrefix(line, "### SEE ALSO") { 184 break 185 } 186 _, _ = fmt.Fprintf(out, "%s\n", line) 187 } 188 } 189 } 190 } 191 } 192 return nil 193 } 194 195 func dieOnError(err error, msg string) { 196 if err != nil { 197 fmt.Printf("[ERROR] %s: %v", msg, err) 198 os.Exit(1) 199 } 200 } 201 202 func buildConfigFromFS(templatesDir string, triggersDir string) (map[string]services.Notification, map[string][]triggers.Condition, error) { 203 templatesCfg := map[string]services.Notification{} 204 err := filepath.Walk(templatesDir, func(p string, info os.FileInfo, e error) error { 205 if e != nil { 206 return fmt.Errorf("error navigating the templates dirctory: %s : %w", templatesDir, e) 207 } 208 if info.IsDir() { 209 return nil 210 } 211 data, err := os.ReadFile(p) 212 if err != nil { 213 return fmt.Errorf("error reading the template file: %s : %w", p, err) 214 } 215 name := strings.Split(path.Base(p), ".")[0] 216 var template services.Notification 217 if err := yaml.Unmarshal(data, &template); err != nil { 218 return fmt.Errorf("error unmarshaling the data from file: %s : %w", p, err) 219 } 220 templatesCfg[name] = template 221 return nil 222 }) 223 if err != nil { 224 return nil, nil, err 225 } 226 227 triggersCfg := map[string][]triggers.Condition{} 228 err = filepath.Walk(triggersDir, func(p string, info os.FileInfo, e error) error { 229 if e != nil { 230 return fmt.Errorf("error navigating the triggers dirctory: %s : %w", triggersDir, e) 231 } 232 if info.IsDir() { 233 return nil 234 } 235 data, err := os.ReadFile(p) 236 if err != nil { 237 return fmt.Errorf("error reading the trigger file: %s : %w", p, err) 238 } 239 name := strings.Split(path.Base(p), ".")[0] 240 var trigger []triggers.Condition 241 if err := yaml.Unmarshal(data, &trigger); err != nil { 242 return fmt.Errorf("error unmarshaling the data from file: %s : %w", p, err) 243 } 244 triggersCfg[name] = trigger 245 return nil 246 }) 247 if err != nil { 248 return nil, nil, err 249 } 250 return templatesCfg, triggersCfg, nil 251 }