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.

compat.go 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. package rpc
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "github.com/weaveworks/flux"
  7. "github.com/weaveworks/flux/api/v10"
  8. "github.com/weaveworks/flux/api/v11"
  9. "github.com/weaveworks/flux/api/v6"
  10. "github.com/weaveworks/flux/cluster"
  11. "github.com/weaveworks/flux/policy"
  12. "github.com/weaveworks/flux/remote"
  13. "github.com/weaveworks/flux/update"
  14. )
  15. func requireServiceSpecKinds(ss update.ResourceSpec, kinds []string) error {
  16. id, err := ss.AsID()
  17. if err != nil {
  18. return nil
  19. }
  20. _, kind, _ := id.Components()
  21. if !contains(kinds, kind) {
  22. return fmt.Errorf("Unsupported resource kind: %s", kind)
  23. }
  24. return nil
  25. }
  26. func requireServiceIDKinds(id flux.ResourceID, kinds []string) error {
  27. _, kind, _ := id.Components()
  28. if !contains(kinds, kind) {
  29. return fmt.Errorf("Unsupported resource kind: %s", kind)
  30. }
  31. return nil
  32. }
  33. func requireSpecKinds(s update.Spec, kinds []string) error {
  34. switch s := s.Spec.(type) {
  35. case policy.Updates:
  36. for id, _ := range s {
  37. _, kind, _ := id.Components()
  38. if !contains(kinds, kind) {
  39. return fmt.Errorf("Unsupported resource kind: %s", kind)
  40. }
  41. }
  42. case update.ReleaseImageSpec:
  43. for _, ss := range s.ServiceSpecs {
  44. if err := requireServiceSpecKinds(ss, kinds); err != nil {
  45. return err
  46. }
  47. }
  48. for _, id := range s.Excludes {
  49. _, kind, _ := id.Components()
  50. if !contains(kinds, kind) {
  51. return fmt.Errorf("Unsupported resource kind: %s", kind)
  52. }
  53. }
  54. }
  55. return nil
  56. }
  57. func contains(ss []string, s string) bool {
  58. for _, x := range ss {
  59. if x == s {
  60. return true
  61. }
  62. }
  63. return false
  64. }
  65. // listServicesRolloutStatus polyfills the rollout status.
  66. func listServicesRolloutStatus(ss []v6.ControllerStatus) {
  67. for i := range ss {
  68. // Polyfill for daemons that list pod information in status ('X out of N updated')
  69. if n, _ := fmt.Sscanf(ss[i].Status, "%d out of %d updated", &ss[i].Rollout.Updated, &ss[i].Rollout.Desired); n == 2 {
  70. // Daemons on an earlier version determined the workload to be ready if updated == desired.
  71. //
  72. // Technically, 'updated' does *not* yet mean these pods are ready and accepting traffic. There
  73. // can still be outdated pods that serve requests. The new way of determining the end of a rollout
  74. // is to make sure the desired count equals to the number of 'available' pods and zero outdated
  75. // pods. To make older daemons reach a "rollout is finished" state we set 'available', 'ready',
  76. // and 'updated' all to the same count.
  77. ss[i].Rollout.Ready = ss[i].Rollout.Updated
  78. ss[i].Rollout.Available = ss[i].Rollout.Updated
  79. ss[i].Status = cluster.StatusUpdating
  80. }
  81. }
  82. }
  83. type listServicesWithoutOptionsClient interface {
  84. ListServices(ctx context.Context, namespace string) ([]v6.ControllerStatus, error)
  85. }
  86. // listServicesWithOptions polyfills the ListServiceWithOptions()
  87. // introduced in v11 by removing unwanted resources after fetching
  88. // all the services.
  89. func listServicesWithOptions(ctx context.Context, p listServicesWithoutOptionsClient, opts v11.ListServicesOptions, supportedKinds []string) ([]v6.ControllerStatus, error) {
  90. if opts.Namespace != "" && len(opts.Services) > 0 {
  91. return nil, errors.New("cannot filter by 'namespace' and 'services' at the same time")
  92. }
  93. if len(supportedKinds) > 0 {
  94. for _, svc := range opts.Services {
  95. if err := requireServiceIDKinds(svc, supportedKinds); err != nil {
  96. return nil, remote.UnsupportedResourceKind(err)
  97. }
  98. }
  99. }
  100. all, err := p.ListServices(ctx, opts.Namespace)
  101. listServicesRolloutStatus(all)
  102. if err != nil {
  103. return nil, err
  104. }
  105. if len(opts.Services) == 0 {
  106. return all, nil
  107. }
  108. // Polyfill the service IDs filter
  109. want := map[flux.ResourceID]struct{}{}
  110. for _, svc := range opts.Services {
  111. want[svc] = struct{}{}
  112. }
  113. var controllers []v6.ControllerStatus
  114. for _, svc := range all {
  115. if _, ok := want[svc.ID]; ok {
  116. controllers = append(controllers, svc)
  117. }
  118. }
  119. return controllers, nil
  120. }
  121. type listImagesWithoutOptionsClient interface {
  122. ListServices(ctx context.Context, namespace string) ([]v6.ControllerStatus, error)
  123. ListImages(ctx context.Context, spec update.ResourceSpec) ([]v6.ImageStatus, error)
  124. }
  125. // listImagesWithOptions is called by ListImagesWithOptions so we can use an
  126. // interface to dispatch .ListImages() and .ListServices() to the correct
  127. // API version.
  128. func listImagesWithOptions(ctx context.Context, client listImagesWithoutOptionsClient, opts v10.ListImagesOptions) ([]v6.ImageStatus, error) {
  129. statuses, err := client.ListImages(ctx, opts.Spec)
  130. if err != nil {
  131. return statuses, err
  132. }
  133. var ns string
  134. if opts.Spec != update.ResourceSpecAll {
  135. resourceID, err := opts.Spec.AsID()
  136. if err != nil {
  137. return statuses, err
  138. }
  139. ns, _, _ = resourceID.Components()
  140. }
  141. services, err := client.ListServices(ctx, ns)
  142. if err != nil {
  143. return statuses, err
  144. }
  145. policyMap := map[flux.ResourceID]map[string]string{}
  146. for _, service := range services {
  147. policyMap[service.ID] = service.Policies
  148. }
  149. // Polyfill container fields from v10
  150. for i, status := range statuses {
  151. for j, container := range status.Containers {
  152. var p policy.Set
  153. if policies, ok := policyMap[status.ID]; ok {
  154. p = policy.Set{}
  155. for k, v := range policies {
  156. p[policy.Policy(k)] = v
  157. }
  158. }
  159. tagPattern := policy.GetTagPattern(p, container.Name)
  160. // Create a new container using the same function used in v10
  161. newContainer, err := v6.NewContainer(container.Name, container.Available, container.Current, tagPattern, opts.OverrideContainerFields)
  162. if err != nil {
  163. return statuses, err
  164. }
  165. statuses[i].Containers[j] = newContainer
  166. }
  167. }
  168. return statuses, nil
  169. }