scalehandler.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. /*
  2. Copyright 2021 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package managedfields
  14. import (
  15. "fmt"
  16. "k8s.io/apimachinery/pkg/api/meta"
  17. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  18. "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
  19. "k8s.io/apimachinery/pkg/runtime/schema"
  20. "k8s.io/apimachinery/pkg/util/managedfields/internal"
  21. "sigs.k8s.io/structured-merge-diff/v4/fieldpath"
  22. )
  23. var (
  24. scaleGroupVersion = schema.GroupVersion{Group: "autoscaling", Version: "v1"}
  25. replicasPathInScale = fieldpath.MakePathOrDie("spec", "replicas")
  26. )
  27. // ResourcePathMappings maps a group/version to its replicas path. The
  28. // assumption is that all the paths correspond to leaf fields.
  29. type ResourcePathMappings map[string]fieldpath.Path
  30. // ScaleHandler manages the conversion of managed fields between a main
  31. // resource and the scale subresource
  32. type ScaleHandler struct {
  33. parentEntries []metav1.ManagedFieldsEntry
  34. groupVersion schema.GroupVersion
  35. mappings ResourcePathMappings
  36. }
  37. // NewScaleHandler creates a new ScaleHandler
  38. func NewScaleHandler(parentEntries []metav1.ManagedFieldsEntry, groupVersion schema.GroupVersion, mappings ResourcePathMappings) *ScaleHandler {
  39. return &ScaleHandler{
  40. parentEntries: parentEntries,
  41. groupVersion: groupVersion,
  42. mappings: mappings,
  43. }
  44. }
  45. // ToSubresource filter the managed fields of the main resource and convert
  46. // them so that they can be handled by scale.
  47. // For the managed fields that have a replicas path it performs two changes:
  48. // 1. APIVersion is changed to the APIVersion of the scale subresource
  49. // 2. Replicas path of the main resource is transformed to the replicas path of
  50. // the scale subresource
  51. func (h *ScaleHandler) ToSubresource() ([]metav1.ManagedFieldsEntry, error) {
  52. managed, err := internal.DecodeManagedFields(h.parentEntries)
  53. if err != nil {
  54. return nil, err
  55. }
  56. f := fieldpath.ManagedFields{}
  57. t := map[string]*metav1.Time{}
  58. for manager, versionedSet := range managed.Fields() {
  59. path, ok := h.mappings[string(versionedSet.APIVersion())]
  60. // Skip the entry if the APIVersion is unknown
  61. if !ok || path == nil {
  62. continue
  63. }
  64. if versionedSet.Set().Has(path) {
  65. newVersionedSet := fieldpath.NewVersionedSet(
  66. fieldpath.NewSet(replicasPathInScale),
  67. fieldpath.APIVersion(scaleGroupVersion.String()),
  68. versionedSet.Applied(),
  69. )
  70. f[manager] = newVersionedSet
  71. t[manager] = managed.Times()[manager]
  72. }
  73. }
  74. return managedFieldsEntries(internal.NewManaged(f, t))
  75. }
  76. // ToParent merges `scaleEntries` with the entries of the main resource and
  77. // transforms them accordingly
  78. func (h *ScaleHandler) ToParent(scaleEntries []metav1.ManagedFieldsEntry) ([]metav1.ManagedFieldsEntry, error) {
  79. decodedParentEntries, err := internal.DecodeManagedFields(h.parentEntries)
  80. if err != nil {
  81. return nil, err
  82. }
  83. parentFields := decodedParentEntries.Fields()
  84. decodedScaleEntries, err := internal.DecodeManagedFields(scaleEntries)
  85. if err != nil {
  86. return nil, err
  87. }
  88. scaleFields := decodedScaleEntries.Fields()
  89. f := fieldpath.ManagedFields{}
  90. t := map[string]*metav1.Time{}
  91. for manager, versionedSet := range parentFields {
  92. // Get the main resource "replicas" path
  93. path, ok := h.mappings[string(versionedSet.APIVersion())]
  94. // Drop the entry if the APIVersion is unknown.
  95. if !ok {
  96. continue
  97. }
  98. // If the parent entry does not have the replicas path or it is nil, just
  99. // keep it as it is. The path is nil for Custom Resources without scale
  100. // subresource.
  101. if path == nil || !versionedSet.Set().Has(path) {
  102. f[manager] = versionedSet
  103. t[manager] = decodedParentEntries.Times()[manager]
  104. continue
  105. }
  106. if _, ok := scaleFields[manager]; !ok {
  107. // "Steal" the replicas path from the main resource entry
  108. newSet := versionedSet.Set().Difference(fieldpath.NewSet(path))
  109. if !newSet.Empty() {
  110. newVersionedSet := fieldpath.NewVersionedSet(
  111. newSet,
  112. versionedSet.APIVersion(),
  113. versionedSet.Applied(),
  114. )
  115. f[manager] = newVersionedSet
  116. t[manager] = decodedParentEntries.Times()[manager]
  117. }
  118. } else {
  119. // Field wasn't stolen, let's keep the entry as it is.
  120. f[manager] = versionedSet
  121. t[manager] = decodedParentEntries.Times()[manager]
  122. delete(scaleFields, manager)
  123. }
  124. }
  125. for manager, versionedSet := range scaleFields {
  126. if !versionedSet.Set().Has(replicasPathInScale) {
  127. continue
  128. }
  129. newVersionedSet := fieldpath.NewVersionedSet(
  130. fieldpath.NewSet(h.mappings[h.groupVersion.String()]),
  131. fieldpath.APIVersion(h.groupVersion.String()),
  132. versionedSet.Applied(),
  133. )
  134. f[manager] = newVersionedSet
  135. t[manager] = decodedParentEntries.Times()[manager]
  136. }
  137. return managedFieldsEntries(internal.NewManaged(f, t))
  138. }
  139. func managedFieldsEntries(entries internal.ManagedInterface) ([]metav1.ManagedFieldsEntry, error) {
  140. obj := &unstructured.Unstructured{Object: map[string]interface{}{}}
  141. if err := internal.EncodeObjectManagedFields(obj, entries); err != nil {
  142. return nil, err
  143. }
  144. accessor, err := meta.Accessor(obj)
  145. if err != nil {
  146. panic(fmt.Sprintf("couldn't get accessor: %v", err))
  147. }
  148. return accessor.GetManagedFields(), nil
  149. }