helper.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  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. "fmt"
  16. "io"
  17. "strings"
  18. "k8s.io/kubernetes/pkg/api"
  19. "k8s.io/kubernetes/pkg/api/errors"
  20. "k8s.io/kubernetes/pkg/kubectl/resource"
  21. "k8s.io/kubernetes/pkg/runtime"
  22. "k8s.io/kubernetes/pkg/util/strategicpatch"
  23. )
  24. // selectContainers allows one or more containers to be matched against a string or wildcard
  25. func selectContainers(containers []api.Container, spec string) ([]*api.Container, []*api.Container) {
  26. out := []*api.Container{}
  27. skipped := []*api.Container{}
  28. for i, c := range containers {
  29. if selectString(c.Name, spec) {
  30. out = append(out, &containers[i])
  31. } else {
  32. skipped = append(skipped, &containers[i])
  33. }
  34. }
  35. return out, skipped
  36. }
  37. // handlePodUpdateError prints a more useful error to the end user when mutating a pod.
  38. func handlePodUpdateError(out io.Writer, err error, resource string) {
  39. if statusError, ok := err.(*errors.StatusError); ok && errors.IsInvalid(err) {
  40. errorDetails := statusError.Status().Details
  41. if errorDetails.Kind == "Pod" {
  42. all, match := true, false
  43. for _, cause := range errorDetails.Causes {
  44. if cause.Field == "spec" && strings.Contains(cause.Message, "may not update fields other than") {
  45. fmt.Fprintf(out, "error: may not update %s in pod %q directly\n", resource, errorDetails.Name)
  46. match = true
  47. } else {
  48. all = false
  49. }
  50. }
  51. if all && match {
  52. return
  53. }
  54. }
  55. }
  56. fmt.Fprintf(out, "error: %v\n", err)
  57. }
  58. // selectString returns true if the provided string matches spec, where spec is a string with
  59. // a non-greedy '*' wildcard operator.
  60. // TODO: turn into a regex and handle greedy matches and backtracking.
  61. func selectString(s, spec string) bool {
  62. if spec == "*" {
  63. return true
  64. }
  65. if !strings.Contains(spec, "*") {
  66. return s == spec
  67. }
  68. pos := 0
  69. match := true
  70. parts := strings.Split(spec, "*")
  71. for i, part := range parts {
  72. if len(part) == 0 {
  73. continue
  74. }
  75. next := strings.Index(s[pos:], part)
  76. switch {
  77. // next part not in string
  78. case next < pos:
  79. fallthrough
  80. // first part does not match start of string
  81. case i == 0 && pos != 0:
  82. fallthrough
  83. // last part does not exactly match remaining part of string
  84. case i == (len(parts)-1) && len(s) != (len(part)+next):
  85. match = false
  86. break
  87. default:
  88. pos = next
  89. }
  90. }
  91. return match
  92. }
  93. // Patch represents the result of a mutation to an object.
  94. type Patch struct {
  95. Info *resource.Info
  96. Err error
  97. Before []byte
  98. After []byte
  99. Patch []byte
  100. }
  101. // CalculatePatches calls the mutation function on each provided info object, and generates a strategic merge patch for
  102. // the changes in the object. Encoder must be able to encode the info into the appropriate destination type. If mutateFn
  103. // returns false, the object is not included in the final list of patches.
  104. func CalculatePatches(infos []*resource.Info, encoder runtime.Encoder, mutateFn func(*resource.Info) (bool, error)) []*Patch {
  105. var patches []*Patch
  106. for _, info := range infos {
  107. patch := &Patch{Info: info}
  108. patch.Before, patch.Err = runtime.Encode(encoder, info.Object)
  109. ok, err := mutateFn(info)
  110. if !ok {
  111. continue
  112. }
  113. if err != nil {
  114. patch.Err = err
  115. }
  116. patches = append(patches, patch)
  117. if patch.Err != nil {
  118. continue
  119. }
  120. patch.After, patch.Err = runtime.Encode(encoder, info.Object)
  121. if patch.Err != nil {
  122. continue
  123. }
  124. // TODO: should be via New
  125. versioned, err := info.Mapping.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupVersion())
  126. if err != nil {
  127. patch.Err = err
  128. continue
  129. }
  130. patch.Patch, patch.Err = strategicpatch.CreateTwoWayMergePatch(patch.Before, patch.After, versioned)
  131. }
  132. return patches
  133. }