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.

policy_cmd.go 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "strings"
  6. "github.com/spf13/cobra"
  7. "github.com/fluxcd/flux/policy"
  8. "github.com/fluxcd/flux/resource"
  9. "github.com/fluxcd/flux/update"
  10. )
  11. type workloadPolicyOpts struct {
  12. *rootOpts
  13. outputOpts
  14. namespace string
  15. workload string
  16. tagAll string
  17. tags []string
  18. automate, deautomate bool
  19. lock, unlock bool
  20. cause update.Cause
  21. // Deprecated
  22. controller string
  23. }
  24. func newWorkloadPolicy(parent *rootOpts) *workloadPolicyOpts {
  25. return &workloadPolicyOpts{rootOpts: parent}
  26. }
  27. func (opts *workloadPolicyOpts) Command() *cobra.Command {
  28. cmd := &cobra.Command{
  29. Use: "policy",
  30. Short: "Manage policies for a workload.",
  31. Long: `
  32. Manage policies for a workload.
  33. Tag filter patterns must be specified as 'container=pattern', such as 'foo=1.*'
  34. where an asterisk means 'match anything'.
  35. Surrounding these with single-quotes are recommended to avoid shell expansion.
  36. If both --tag-all and --tag are specified, --tag-all will apply to all
  37. containers which aren't explicitly named.
  38. `,
  39. Example: makeExample(
  40. "fluxctl policy --workload=default:deployment/foo --automate",
  41. "fluxctl policy --workload=default:deployment/foo --lock",
  42. "fluxctl policy --workload=default:deployment/foo --tag='bar=1.*' --tag='baz=2.*'",
  43. "fluxctl policy --workload=default:deployment/foo --tag-all='master-*' --tag='bar=1.*'",
  44. ),
  45. RunE: opts.RunE,
  46. }
  47. AddOutputFlags(cmd, &opts.outputOpts)
  48. AddCauseFlags(cmd, &opts.cause)
  49. flags := cmd.Flags()
  50. flags.StringVarP(&opts.namespace, "namespace", "n", getKubeConfigContextNamespace("default"), "Workload namespace")
  51. flags.StringVarP(&opts.workload, "workload", "w", "", "Workload to modify")
  52. flags.StringVar(&opts.tagAll, "tag-all", "", "Tag filter pattern to apply to all containers")
  53. flags.StringSliceVar(&opts.tags, "tag", nil, "Tag filter container/pattern pairs")
  54. flags.BoolVar(&opts.automate, "automate", false, "Automate workload")
  55. flags.BoolVar(&opts.deautomate, "deautomate", false, "Deautomate workload")
  56. flags.BoolVar(&opts.lock, "lock", false, "Lock workload")
  57. flags.BoolVar(&opts.unlock, "unlock", false, "Unlock workload")
  58. // Deprecated
  59. flags.StringVarP(&opts.controller, "controller", "c", "", "Controller to modify")
  60. flags.MarkDeprecated("controller", "changed to --workload, use that instead")
  61. return cmd
  62. }
  63. func (opts *workloadPolicyOpts) RunE(cmd *cobra.Command, args []string) error {
  64. if len(args) > 0 {
  65. return errorWantedNoArgs
  66. }
  67. // Backwards compatibility with --controller until we remove it
  68. switch {
  69. case opts.workload == "" && opts.controller == "":
  70. return newUsageError("-w, --workload is required")
  71. case opts.workload != "" && opts.controller != "":
  72. return newUsageError("can't specify both the controller and workload")
  73. case opts.controller != "":
  74. opts.workload = opts.controller
  75. }
  76. if opts.automate && opts.deautomate {
  77. return newUsageError("automate and deautomate both specified")
  78. }
  79. if opts.lock && opts.unlock {
  80. return newUsageError("lock and unlock both specified")
  81. }
  82. resourceID, err := resource.ParseIDOptionalNamespace(opts.namespace, opts.workload)
  83. if err != nil {
  84. return err
  85. }
  86. changes, err := calculatePolicyChanges(opts)
  87. if err != nil {
  88. return err
  89. }
  90. ctx := context.Background()
  91. updates := resource.PolicyUpdates{
  92. resourceID: changes,
  93. }
  94. jobID, err := opts.API.UpdateManifests(ctx, update.Spec{
  95. Type: update.Policy,
  96. Cause: opts.cause,
  97. Spec: updates,
  98. })
  99. if err != nil {
  100. return err
  101. }
  102. return await(ctx, cmd.OutOrStdout(), cmd.OutOrStderr(), opts.API, jobID, false, opts.verbosity, opts.Timeout)
  103. }
  104. func calculatePolicyChanges(opts *workloadPolicyOpts) (resource.PolicyUpdate, error) {
  105. add := policy.Set{}
  106. if opts.automate {
  107. add = add.Add(policy.Automated)
  108. }
  109. if opts.lock {
  110. add = add.Add(policy.Locked)
  111. if opts.cause.User != "" {
  112. add = add.
  113. Set(policy.LockedUser, opts.cause.User).
  114. Set(policy.LockedMsg, opts.cause.Message)
  115. }
  116. }
  117. remove := policy.Set{}
  118. if opts.deautomate {
  119. remove = remove.Add(policy.Automated)
  120. }
  121. if opts.unlock {
  122. remove = remove.
  123. Add(policy.Locked).
  124. Add(policy.LockedMsg).
  125. Add(policy.LockedUser)
  126. }
  127. if opts.tagAll != "" {
  128. add = add.Set(policy.TagAll, policy.NewPattern(opts.tagAll).String())
  129. }
  130. for _, tagPair := range opts.tags {
  131. parts := strings.Split(tagPair, "=")
  132. if len(parts) != 2 {
  133. return resource.PolicyUpdate{}, fmt.Errorf("invalid container/tag pair: %q. Expected format is 'container=filter'", tagPair)
  134. }
  135. container, tag := parts[0], parts[1]
  136. if tag != "*" {
  137. add = add.Set(policy.TagPrefix(container), policy.NewPattern(tag).String())
  138. } else {
  139. remove = remove.Add(policy.TagPrefix(container))
  140. }
  141. }
  142. return resource.PolicyUpdate{
  143. Add: add,
  144. Remove: remove,
  145. }, nil
  146. }