GitOps for k8s
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

root_cmd.go 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. package main
  2. import (
  3. "fmt"
  4. "net/http"
  5. "net/url"
  6. "os"
  7. "strings"
  8. "time"
  9. "github.com/pkg/errors"
  10. "github.com/spf13/cobra"
  11. "github.com/spf13/pflag"
  12. "github.com/fluxcd/flux/api"
  13. transport "github.com/fluxcd/flux/http"
  14. "github.com/fluxcd/flux/http/client"
  15. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  16. )
  17. type rootOpts struct {
  18. URL string
  19. Token string
  20. Namespace string
  21. Labels map[string]string
  22. API api.Server
  23. Timeout time.Duration
  24. }
  25. func newRoot() *rootOpts {
  26. return &rootOpts{}
  27. }
  28. var rootLongHelp = strings.TrimSpace(`
  29. fluxctl helps you deploy your code.
  30. Connecting:
  31. # To a fluxd running in namespace "default" in your current kubectl context
  32. fluxctl list-workloads
  33. # To a fluxd running in namespace "weave" in your current kubectl context
  34. fluxctl --k8s-fwd-ns=weave list-workloads
  35. # To a Weave Cloud instance, with your instance token in $TOKEN
  36. fluxctl --token $TOKEN list-workloads
  37. Workflow:
  38. fluxctl list-workloads # Which workloads are running?
  39. fluxctl list-images --workload=default:deployment/foo # Which images are running/available?
  40. fluxctl release --workload=default:deployment/foo --update-image=bar:v2 # Release new version.
  41. `)
  42. const (
  43. defaultURLGivenToken = "https://cloud.weave.works/api/flux"
  44. envVariableURL = "FLUX_URL"
  45. envVariableNamespace = "FLUX_FORWARD_NAMESPACE"
  46. envVariableLabels = "FLUX_FORWARD_LABELS"
  47. envVariableToken = "FLUX_SERVICE_TOKEN"
  48. envVariableCloudToken = "WEAVE_CLOUD_TOKEN"
  49. envVariableTimeout = "FLUX_TIMEOUT"
  50. )
  51. func (opts *rootOpts) Command() *cobra.Command {
  52. cmd := &cobra.Command{
  53. Use: "fluxctl",
  54. Long: rootLongHelp,
  55. SilenceUsage: true,
  56. SilenceErrors: true,
  57. PersistentPreRunE: opts.PersistentPreRunE,
  58. }
  59. cmd.PersistentFlags().StringVar(&opts.Namespace, "k8s-fwd-ns", "default",
  60. fmt.Sprintf("Namespace in which fluxd is running, for creating a port forward to access the API. No port forward will be created if a URL or token is given. You can also set the environment variable %s", envVariableNamespace))
  61. cmd.PersistentFlags().StringToStringVar(&opts.Labels, "k8s-fwd-labels", map[string]string{"app": "flux"},
  62. fmt.Sprintf("Labels used to select the fluxd pod a port forward should be created for. You can also set the environment variable %s", envVariableLabels))
  63. cmd.PersistentFlags().StringVarP(&opts.URL, "url", "u", "",
  64. fmt.Sprintf("Base URL of the Flux API (defaults to %q if a token is provided); you can also set the environment variable %s", defaultURLGivenToken, envVariableURL))
  65. cmd.PersistentFlags().StringVarP(&opts.Token, "token", "t", "",
  66. fmt.Sprintf("Weave Cloud authentication token; you can also set the environment variable %s or %s", envVariableCloudToken, envVariableToken))
  67. cmd.PersistentFlags().DurationVar(&opts.Timeout, "timeout", 60*time.Second,
  68. fmt.Sprintf("Global command timeout; you can also set the environment variable %s", envVariableTimeout))
  69. cmd.AddCommand(
  70. newVersionCommand(),
  71. newImageList(opts).Command(),
  72. newWorkloadList(opts).Command(),
  73. newWorkloadRelease(opts).Command(),
  74. newWorkloadAutomate(opts).Command(),
  75. newWorkloadDeautomate(opts).Command(),
  76. newWorkloadLock(opts).Command(),
  77. newWorkloadUnlock(opts).Command(),
  78. newWorkloadPolicy(opts).Command(),
  79. newSave(opts).Command(),
  80. newIdentity(opts).Command(),
  81. newSync(opts).Command(),
  82. newInstall().Command(),
  83. )
  84. return cmd
  85. }
  86. func (opts *rootOpts) PersistentPreRunE(cmd *cobra.Command, _ []string) error {
  87. // skip port forward for certain commands
  88. switch cmd.Use {
  89. case "version":
  90. fallthrough
  91. case "install":
  92. return nil
  93. }
  94. setFromEnvIfNotSet(cmd.Flags(), "k8s-fwd-ns", envVariableNamespace)
  95. setFromEnvIfNotSet(cmd.Flags(), "k8s-fwd-labels", envVariableLabels)
  96. setFromEnvIfNotSet(cmd.Flags(), "token", envVariableToken, envVariableCloudToken)
  97. setFromEnvIfNotSet(cmd.Flags(), "url", envVariableURL)
  98. setFromEnvIfNotSet(cmd.Flags(), "timeout", envVariableTimeout)
  99. if opts.Token != "" && opts.URL == "" {
  100. opts.URL = defaultURLGivenToken
  101. }
  102. if opts.URL == "" {
  103. portforwarder, err := tryPortforwards(opts.Namespace, metav1.LabelSelector{
  104. MatchLabels: opts.Labels,
  105. }, metav1.LabelSelector{
  106. MatchExpressions: []metav1.LabelSelectorRequirement{
  107. metav1.LabelSelectorRequirement{
  108. Key: "name",
  109. Operator: metav1.LabelSelectorOpIn,
  110. Values: []string{"flux", "fluxd", "weave-flux-agent"},
  111. },
  112. },
  113. })
  114. if err != nil {
  115. return err
  116. }
  117. opts.URL = fmt.Sprintf("http://127.0.0.1:%d/api/flux", portforwarder.ListenPort)
  118. }
  119. if _, err := url.Parse(opts.URL); err != nil {
  120. return errors.Wrapf(err, "parsing URL")
  121. }
  122. opts.API = client.New(http.DefaultClient, transport.NewAPIRouter(), opts.URL, client.Token(opts.Token))
  123. return nil
  124. }
  125. func setFromEnvIfNotSet(flags *pflag.FlagSet, flagName string, envNames ...string) {
  126. if flags.Changed(flagName) {
  127. return
  128. }
  129. for _, envName := range envNames {
  130. if env := os.Getenv(envName); env != "" {
  131. flags.Set(flagName, env)
  132. }
  133. }
  134. }