commands.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. package cmd
  2. import (
  3. "fmt"
  4. "os"
  5. "github.com/kubecost/cost-model/pkg/cmd/agent"
  6. "github.com/kubecost/cost-model/pkg/cmd/costmodel"
  7. "github.com/spf13/cobra"
  8. )
  9. const (
  10. // commandRoot is the root command used to route to sub-commands
  11. commandRoot string = "root"
  12. // CommandCostModel is the command used to execute the metrics emission and ETL pipeline
  13. CommandCostModel string = "cost-model"
  14. // CommandAgent executes the application in agent mode, which provides only metrics exporting.
  15. CommandAgent string = "agent"
  16. )
  17. // Execute runs the root command for the application. By default, if no command argument is provided,
  18. // on the command line, the cost-model is executed by default.
  19. //
  20. // This function accepts a costModelCmd parameter to provide support for various cost-model implementations
  21. // (ie: open source, enterprise).
  22. func Execute(costModelCmd *cobra.Command) error {
  23. // use the open-source cost-model if a command is not provided
  24. if costModelCmd == nil {
  25. costModelCmd = newCostModelCommand()
  26. }
  27. // validate the command being passed
  28. if err := validate(costModelCmd); err != nil {
  29. return err
  30. }
  31. rootCmd := newRootCommand(costModelCmd)
  32. // in the event that no directive/command is passed, we want to default to using the cost-model command
  33. // cobra doesn't provide a way within the API to do this, so we'll prepend the command if it is omitted.
  34. if len(os.Args) > 1 {
  35. // try to find the sub-command from the arguments, if there's an error or the command _is_ the
  36. // root command, prepend the default command
  37. pCmd, _, err := rootCmd.Find(os.Args[1:])
  38. if err != nil || pCmd.Use == rootCmd.Use {
  39. rootCmd.SetArgs(append([]string{CommandCostModel}, os.Args[1:]...))
  40. }
  41. } else {
  42. rootCmd.SetArgs([]string{CommandCostModel})
  43. }
  44. return rootCmd.Execute()
  45. }
  46. // newRootCommand creates a new root command which will act as a sub-command router for the
  47. // cost-model application
  48. func newRootCommand(costModelCmd *cobra.Command) *cobra.Command {
  49. cmd := &cobra.Command{
  50. Use: commandRoot,
  51. SilenceUsage: true,
  52. }
  53. // add the modes of operation
  54. cmd.AddCommand(
  55. costModelCmd,
  56. newAgentCommand(),
  57. )
  58. return cmd
  59. }
  60. // default open-source cost-model command
  61. func newCostModelCommand() *cobra.Command {
  62. opts := &costmodel.CostModelOpts{}
  63. cmCmd := &cobra.Command{
  64. Use: CommandCostModel,
  65. Short: "Cost-Model metric exporter and data aggregator.",
  66. RunE: func(cmd *cobra.Command, args []string) error {
  67. return costmodel.Execute(opts)
  68. },
  69. }
  70. // TODO: We could introduce a way of mapping input command-line parameters to a configuration
  71. // TODO: object, and pass that object to the agent Execute()
  72. // cmCmd.Flags().<Type>VarP(&opts.<Property>, "<flag>", "<short>", <default>, "<usage>")
  73. return cmCmd
  74. }
  75. func newAgentCommand() *cobra.Command {
  76. opts := &agent.AgentOpts{}
  77. agentCmd := &cobra.Command{
  78. Use: CommandAgent,
  79. Short: "Agent mode operates as a metric exporter only.",
  80. RunE: func(cmd *cobra.Command, args []string) error {
  81. return agent.Execute(opts)
  82. },
  83. }
  84. // TODO: We could introduce a way of mapping input command-line parameters to a configuration
  85. // TODO: object, and pass that object to the agent Execute()
  86. // agentCmd.Flags().<Type>VarP(&opts.<Property>, "<flag>", "<short>", <default>, "<usage>")
  87. return agentCmd
  88. }
  89. // validate will check to ensure that the cost model command passed in has a use equal to the
  90. // CommandCostModel to ensure that the default command matches.
  91. func validate(costModelCommand *cobra.Command) error {
  92. if costModelCommand.Use != CommandCostModel {
  93. return fmt.Errorf("Incompatible 'cost-model' command provided to run-time.")
  94. }
  95. return nil
  96. }