helper.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /*
  2. Copyright 2016 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 set
  14. import (
  15. "strings"
  16. "k8s.io/api/core/v1"
  17. "k8s.io/apimachinery/pkg/runtime"
  18. "k8s.io/apimachinery/pkg/util/sets"
  19. "k8s.io/apimachinery/pkg/util/strategicpatch"
  20. "k8s.io/cli-runtime/pkg/resource"
  21. )
  22. // selectContainers allows one or more containers to be matched against a string or wildcard
  23. func selectContainers(containers []v1.Container, spec string) ([]*v1.Container, []*v1.Container) {
  24. out := []*v1.Container{}
  25. skipped := []*v1.Container{}
  26. for i, c := range containers {
  27. if selectString(c.Name, spec) {
  28. out = append(out, &containers[i])
  29. } else {
  30. skipped = append(skipped, &containers[i])
  31. }
  32. }
  33. return out, skipped
  34. }
  35. // selectString returns true if the provided string matches spec, where spec is a string with
  36. // a non-greedy '*' wildcard operator.
  37. // TODO: turn into a regex and handle greedy matches and backtracking.
  38. func selectString(s, spec string) bool {
  39. if spec == "*" {
  40. return true
  41. }
  42. if !strings.Contains(spec, "*") {
  43. return s == spec
  44. }
  45. pos := 0
  46. match := true
  47. parts := strings.Split(spec, "*")
  48. for i, part := range parts {
  49. if len(part) == 0 {
  50. continue
  51. }
  52. next := strings.Index(s[pos:], part)
  53. switch {
  54. // next part not in string
  55. case next < pos:
  56. fallthrough
  57. // first part does not match start of string
  58. case i == 0 && pos != 0:
  59. fallthrough
  60. // last part does not exactly match remaining part of string
  61. case i == (len(parts)-1) && len(s) != (len(part)+next):
  62. match = false
  63. break
  64. default:
  65. pos = next
  66. }
  67. }
  68. return match
  69. }
  70. // Patch represents the result of a mutation to an object.
  71. type Patch struct {
  72. Info *resource.Info
  73. Err error
  74. Before []byte
  75. After []byte
  76. Patch []byte
  77. }
  78. // PatchFn is a function type that accepts an info object and returns a byte slice.
  79. // Implementations of PatchFn should update the object and return it encoded.
  80. type PatchFn func(runtime.Object) ([]byte, error)
  81. // CalculatePatch calls the mutation function on the provided info object, and generates a strategic merge patch for
  82. // the changes in the object. Encoder must be able to encode the info into the appropriate destination type.
  83. // This function returns whether the mutation function made any change in the original object.
  84. func CalculatePatch(patch *Patch, encoder runtime.Encoder, mutateFn PatchFn) bool {
  85. patch.Before, patch.Err = runtime.Encode(encoder, patch.Info.Object)
  86. patch.After, patch.Err = mutateFn(patch.Info.Object)
  87. if patch.Err != nil {
  88. return true
  89. }
  90. if patch.After == nil {
  91. return false
  92. }
  93. patch.Patch, patch.Err = strategicpatch.CreateTwoWayMergePatch(patch.Before, patch.After, patch.Info.Object)
  94. return true
  95. }
  96. // CalculatePatches calculates patches on each provided info object. If the provided mutateFn
  97. // makes no change in an object, the object is not included in the final list of patches.
  98. func CalculatePatches(infos []*resource.Info, encoder runtime.Encoder, mutateFn PatchFn) []*Patch {
  99. var patches []*Patch
  100. for _, info := range infos {
  101. patch := &Patch{Info: info}
  102. if CalculatePatch(patch, encoder, mutateFn) {
  103. patches = append(patches, patch)
  104. }
  105. }
  106. return patches
  107. }
  108. func findEnv(env []v1.EnvVar, name string) (v1.EnvVar, bool) {
  109. for _, e := range env {
  110. if e.Name == name {
  111. return e, true
  112. }
  113. }
  114. return v1.EnvVar{}, false
  115. }
  116. func updateEnv(existing []v1.EnvVar, env []v1.EnvVar, remove []string) []v1.EnvVar {
  117. out := []v1.EnvVar{}
  118. covered := sets.NewString(remove...)
  119. for _, e := range existing {
  120. if covered.Has(e.Name) {
  121. continue
  122. }
  123. newer, ok := findEnv(env, e.Name)
  124. if ok {
  125. covered.Insert(e.Name)
  126. out = append(out, newer)
  127. continue
  128. }
  129. out = append(out, e)
  130. }
  131. for _, e := range env {
  132. if covered.Has(e.Name) {
  133. continue
  134. }
  135. covered.Insert(e.Name)
  136. out = append(out, e)
  137. }
  138. return out
  139. }