package main import ( "context" "fmt" "io" "strings" "time" "git.zb-server.de/Sebi/ssh-netbox-wrapper/internal/cache" "git.zb-server.de/Sebi/ssh-netbox-wrapper/internal/config" "git.zb-server.de/Sebi/ssh-netbox-wrapper/internal/netbox" "git.zb-server.de/Sebi/ssh-netbox-wrapper/internal/notes" "github.com/spf13/cobra" ) func notesCmd() *cobra.Command { var remote bool var kind string cmd := &cobra.Command{Use: "notes", Short: "Read and write server notes (local by default)"} cmd.PersistentFlags().BoolVar(&remote, "netbox", false, "Use the NetBox comments field instead of local notes") cmd.PersistentFlags().StringVar(&kind, "kind", "", "Host kind: device or vm (required for uncached local hosts)") for _, action := range []string{"show", "set", "append"} { sub := &cobra.Command{Use: action + " [text]", Short: action + " a server note", Args: cobra.RangeArgs(1, 2)} if action == "show" { sub.Args = cobra.ExactArgs(1) } sub.RunE = func(cmd *cobra.Command, args []string) error { if kind != "" && kind != "vm" && kind != "device" { return fmt.Errorf("invalid kind %q", kind) } cfg, err := config.Load() if err != nil { return err } var client *netbox.Client if cfg.NetBox.URL != "" { client = netbox.NewClient(cfg.NetBox.URL, cfg.NetBox.Token, cfg.NetBox.TokenVersion) } host := netbox.HostEntry{Name: args[0], Kind: kind} c := cache.New(cfg.Cache.Path, cfg.Cache.TTL) if err := c.Load(); err != nil { return err } var cached []cache.Entry if !remote { cached = c.All() } for _, entry := range cached { if strings.EqualFold(entry.Name, host.Name) && (kind == "" || kind == entry.Kind) { host.Name = entry.Name host.Kind = entry.Kind break } } if !remote && host.Kind == "" { return fmt.Errorf("host is not cached; specify --kind device or --kind vm") } ctx, cancel := context.WithTimeout(cmd.Context(), 20*time.Second) defer cancel() service := notes.New(cfg.NetBox.URL, client) note, err := service.Read(ctx, host, remote) if err != nil { return err } if action == "show" { _, err := fmt.Fprint(cmd.OutOrStdout(), note.Text) return err } var text string if len(args) == 2 { text = args[1] } else { data, err := io.ReadAll(io.LimitReader(cmd.InOrStdin(), 1024*1024+1)) if err != nil { return err } if len(data) > 1024*1024 { return fmt.Errorf("note exceeds 1 MiB") } text = string(data) } if action == "append" && note.Text != "" { note.Text = strings.TrimRight(note.Text, "\n") + "\n" + text } else { note.Text = text } return service.Write(ctx, note, remote) } cmd.AddCommand(sub) } return cmd }