namereference.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /*
  2. Copyright 2018 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 transformers
  14. import (
  15. "fmt"
  16. "log"
  17. "sigs.k8s.io/kustomize/pkg/gvk"
  18. "sigs.k8s.io/kustomize/pkg/resmap"
  19. "sigs.k8s.io/kustomize/pkg/transformers/config"
  20. )
  21. type nameReferenceTransformer struct {
  22. backRefs []config.NameBackReferences
  23. }
  24. var _ Transformer = &nameReferenceTransformer{}
  25. // NewNameReferenceTransformer constructs a nameReferenceTransformer
  26. // with a given slice of NameBackReferences.
  27. func NewNameReferenceTransformer(br []config.NameBackReferences) Transformer {
  28. if br == nil {
  29. log.Fatal("backrefs not expected to be nil")
  30. }
  31. return &nameReferenceTransformer{backRefs: br}
  32. }
  33. // Transform updates name references in resource A that refer to resource B,
  34. // given that B's name may have changed.
  35. //
  36. // For example, a HorizontalPodAutoscaler (HPA) necessarily refers to a
  37. // Deployment (the thing that the HPA scales). The Deployment name might change
  38. // (e.g. prefix added), and the reference in the HPA has to be fixed.
  39. //
  40. // In the outer loop below, we encounter an HPA. In scanning backrefs, we
  41. // find that HPA refers to a Deployment. So we find all resources in the same
  42. // namespace as the HPA (and with the same prefix and suffix), and look through
  43. // them to find all the Deployments with a resId that has a Name matching the
  44. // field in HPA. For each match, we overwrite the HPA name field with the value
  45. // found in the Deployment's name field (the name in the raw object - the
  46. // modified name - not the unmodified name in the resId).
  47. //
  48. // This assumes that the name stored in a ResId (the ResMap key) isn't modified
  49. // by name transformers. Name transformers should only modify the name in the
  50. // body of the resource object (the value in the ResMap).
  51. func (o *nameReferenceTransformer) Transform(m resmap.ResMap) error {
  52. // TODO: Too much looping.
  53. // Even more hidden loops in FilterBy,
  54. // updateNameReference and FindByGVKN.
  55. for id := range m {
  56. for _, backRef := range o.backRefs {
  57. for _, fSpec := range backRef.FieldSpecs {
  58. if id.Gvk().IsSelected(&fSpec.Gvk) {
  59. err := mutateField(
  60. m[id].Map(), fSpec.PathSlice(),
  61. fSpec.CreateIfNotPresent,
  62. o.updateNameReference(
  63. backRef.Gvk, m.FilterBy(id)))
  64. if err != nil {
  65. return err
  66. }
  67. }
  68. }
  69. }
  70. }
  71. return nil
  72. }
  73. func (o *nameReferenceTransformer) updateNameReference(
  74. backRef gvk.Gvk, m resmap.ResMap) func(in interface{}) (interface{}, error) {
  75. return func(in interface{}) (interface{}, error) {
  76. switch in.(type) {
  77. case string:
  78. s, _ := in.(string)
  79. for id, res := range m {
  80. if id.Gvk().IsSelected(&backRef) && id.Name() == s {
  81. matchedIds := m.GetMatchingIds(id.GvknEquals)
  82. // If there's more than one match, there's no way
  83. // to know which one to pick, so emit error.
  84. if len(matchedIds) > 1 {
  85. return nil, fmt.Errorf(
  86. "Multiple matches for name %s:\n %v", id, matchedIds)
  87. }
  88. // Return transformed name of the object,
  89. // complete with prefixes, hashes, etc.
  90. return res.GetName(), nil
  91. }
  92. }
  93. return in, nil
  94. case []interface{}:
  95. l, _ := in.([]interface{})
  96. var names []string
  97. for _, item := range l {
  98. name, ok := item.(string)
  99. if !ok {
  100. return nil, fmt.Errorf("%#v is expected to be %T", item, name)
  101. }
  102. names = append(names, name)
  103. }
  104. for id, res := range m {
  105. indexes := indexOf(id.Name(), names)
  106. if id.Gvk().IsSelected(&backRef) && len(indexes) > 0 {
  107. matchedIds := m.GetMatchingIds(id.GvknEquals)
  108. if len(matchedIds) > 1 {
  109. return nil, fmt.Errorf(
  110. "Multiple matches for name %s:\n %v", id, matchedIds)
  111. }
  112. for _, index := range indexes {
  113. l[index] = res.GetName()
  114. }
  115. return l, nil
  116. }
  117. }
  118. return in, nil
  119. default:
  120. return nil, fmt.Errorf("%#v is expected to be either a string or a []interface{}", in)
  121. }
  122. }
  123. }
  124. func indexOf(s string, slice []string) []int {
  125. var index []int
  126. for i, item := range slice {
  127. if item == s {
  128. index = append(index, i)
  129. }
  130. }
  131. return index
  132. }