root.go 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. // Package cmd holds the cobra command tree.
  2. package cmd
  3. import (
  4. "fmt"
  5. "os"
  6. "github.com/spf13/cobra"
  7. "schlundtech-dns/internal/api"
  8. "schlundtech-dns/internal/config"
  9. "schlundtech-dns/internal/output"
  10. )
  11. // global flags shared by every command.
  12. var (
  13. flagOutput string
  14. flagEndpoint string
  15. flagDryRun bool
  16. )
  17. var rootCmd = &cobra.Command{
  18. Use: "schlundtech-dns",
  19. Short: "Manage Schlundtech DNS zones and records",
  20. Long: `schlundtech-dns talks to the Schlundtech DNS gateway and lets you list your
  21. zones, read their records and change them.
  22. Credentials come from the environment:
  23. SCHLUNDTECH_USER the gateway user
  24. SCHLUNDTECH_PASSWORD the gateway password
  25. SCHLUNDTECH_CONTEXT the project the records belong to (Schlundtech uses 10)
  26. SCHLUNDTECH_TOKEN optional second-factor token, if 2FA is enabled
  27. DNS changes are public and propagate within seconds, so every command that
  28. writes has a --dry-run flag. Use it first.`,
  29. Example: ` export SCHLUNDTECH_USER=... SCHLUNDTECH_PASSWORD=... SCHLUNDTECH_CONTEXT=10
  30. schlundtech-dns zones
  31. schlundtech-dns records list example.com
  32. schlundtech-dns records set example.com --name @ --type TXT --value 'v=spf1 -all' --dry-run`,
  33. SilenceUsage: true,
  34. SilenceErrors: true,
  35. }
  36. func init() {
  37. rootCmd.PersistentFlags().StringVar(&flagOutput, "output", "table", "output format: table or json")
  38. rootCmd.PersistentFlags().StringVar(&flagEndpoint, "endpoint", "", "override the gateway URL (default "+api.DefaultEndpoint+")")
  39. rootCmd.PersistentFlags().BoolVar(&flagDryRun, "dry-run", false, "show what would change without sending it")
  40. }
  41. // Execute runs the command tree. Errors go to stderr so that stdout stays
  42. // pipeable.
  43. func Execute() {
  44. if err := rootCmd.Execute(); err != nil {
  45. fmt.Fprintln(os.Stderr, "error:", err)
  46. os.Exit(1)
  47. }
  48. }
  49. // format returns the parsed output format.
  50. func format() (output.Format, error) { return output.Parse(flagOutput) }
  51. // client builds an API client from the environment. Commands that do not talk
  52. // to the gateway never call it, so --help works without credentials.
  53. func client() (*api.Client, error) {
  54. creds, err := config.FromEnv()
  55. if err != nil {
  56. return nil, err
  57. }
  58. endpoint := flagEndpoint
  59. if endpoint == "" {
  60. endpoint = creds.Endpoint
  61. }
  62. opts := []api.Option{}
  63. if endpoint != "" {
  64. opts = append(opts, api.WithEndpoint(endpoint))
  65. }
  66. return api.New(api.Credentials{
  67. User: creds.User,
  68. Password: creds.Password,
  69. Context: creds.Context,
  70. Token: creds.Token,
  71. }, opts...), nil
  72. }