env_resolve.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. /*
  2. Copyright 2017 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 env
  14. import (
  15. "fmt"
  16. "math"
  17. "strconv"
  18. "strings"
  19. corev1 "k8s.io/api/core/v1"
  20. "k8s.io/apimachinery/pkg/api/meta"
  21. "k8s.io/apimachinery/pkg/api/resource"
  22. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  23. "k8s.io/apimachinery/pkg/runtime"
  24. "k8s.io/apimachinery/pkg/util/sets"
  25. "k8s.io/apimachinery/pkg/util/validation"
  26. "k8s.io/client-go/kubernetes"
  27. )
  28. // ResourceStore defines a new resource store data structure.
  29. type ResourceStore struct {
  30. SecretStore map[string]*corev1.Secret
  31. ConfigMapStore map[string]*corev1.ConfigMap
  32. }
  33. // NewResourceStore returns a pointer to a new resource store data structure.
  34. func NewResourceStore() *ResourceStore {
  35. return &ResourceStore{
  36. SecretStore: make(map[string]*corev1.Secret),
  37. ConfigMapStore: make(map[string]*corev1.ConfigMap),
  38. }
  39. }
  40. // getSecretRefValue returns the value of a secret in the supplied namespace
  41. func getSecretRefValue(client kubernetes.Interface, namespace string, store *ResourceStore, secretSelector *corev1.SecretKeySelector) (string, error) {
  42. secret, ok := store.SecretStore[secretSelector.Name]
  43. if !ok {
  44. var err error
  45. secret, err = client.CoreV1().Secrets(namespace).Get(secretSelector.Name, metav1.GetOptions{})
  46. if err != nil {
  47. return "", err
  48. }
  49. store.SecretStore[secretSelector.Name] = secret
  50. }
  51. if data, ok := secret.Data[secretSelector.Key]; ok {
  52. return string(data), nil
  53. }
  54. return "", fmt.Errorf("key %s not found in secret %s", secretSelector.Key, secretSelector.Name)
  55. }
  56. // getConfigMapRefValue returns the value of a configmap in the supplied namespace
  57. func getConfigMapRefValue(client kubernetes.Interface, namespace string, store *ResourceStore, configMapSelector *corev1.ConfigMapKeySelector) (string, error) {
  58. configMap, ok := store.ConfigMapStore[configMapSelector.Name]
  59. if !ok {
  60. var err error
  61. configMap, err = client.CoreV1().ConfigMaps(namespace).Get(configMapSelector.Name, metav1.GetOptions{})
  62. if err != nil {
  63. return "", err
  64. }
  65. store.ConfigMapStore[configMapSelector.Name] = configMap
  66. }
  67. if data, ok := configMap.Data[configMapSelector.Key]; ok {
  68. return string(data), nil
  69. }
  70. return "", fmt.Errorf("key %s not found in config map %s", configMapSelector.Key, configMapSelector.Name)
  71. }
  72. // getFieldRef returns the value of the supplied path in the given object
  73. func getFieldRef(obj runtime.Object, from *corev1.EnvVarSource) (string, error) {
  74. return extractFieldPathAsString(obj, from.FieldRef.FieldPath)
  75. }
  76. // extractFieldPathAsString extracts the field from the given object
  77. // and returns it as a string. The object must be a pointer to an
  78. // API type.
  79. func extractFieldPathAsString(obj interface{}, fieldPath string) (string, error) {
  80. accessor, err := meta.Accessor(obj)
  81. if err != nil {
  82. return "", nil
  83. }
  84. if path, subscript, ok := splitMaybeSubscriptedPath(fieldPath); ok {
  85. switch path {
  86. case "metadata.annotations":
  87. if errs := validation.IsQualifiedName(strings.ToLower(subscript)); len(errs) != 0 {
  88. return "", fmt.Errorf("invalid key subscript in %s: %s", fieldPath, strings.Join(errs, ";"))
  89. }
  90. return accessor.GetAnnotations()[subscript], nil
  91. case "metadata.labels":
  92. if errs := validation.IsQualifiedName(subscript); len(errs) != 0 {
  93. return "", fmt.Errorf("invalid key subscript in %s: %s", fieldPath, strings.Join(errs, ";"))
  94. }
  95. return accessor.GetLabels()[subscript], nil
  96. default:
  97. return "", fmt.Errorf("fieldPath %q does not support subscript", fieldPath)
  98. }
  99. }
  100. switch fieldPath {
  101. case "metadata.annotations":
  102. return formatMap(accessor.GetAnnotations()), nil
  103. case "metadata.labels":
  104. return formatMap(accessor.GetLabels()), nil
  105. case "metadata.name":
  106. return accessor.GetName(), nil
  107. case "metadata.namespace":
  108. return accessor.GetNamespace(), nil
  109. case "metadata.uid":
  110. return string(accessor.GetUID()), nil
  111. }
  112. return "", fmt.Errorf("unsupported fieldPath: %v", fieldPath)
  113. }
  114. // splitMaybeSubscriptedPath checks whether the specified fieldPath is
  115. // subscripted, and
  116. // - if yes, this function splits the fieldPath into path and subscript, and
  117. // returns (path, subscript, true).
  118. // - if no, this function returns (fieldPath, "", false).
  119. //
  120. // Example inputs and outputs:
  121. // - "metadata.annotations['myKey']" --> ("metadata.annotations", "myKey", true)
  122. // - "metadata.annotations['a[b]c']" --> ("metadata.annotations", "a[b]c", true)
  123. // - "metadata.labels['']" --> ("metadata.labels", "", true)
  124. // - "metadata.labels" --> ("metadata.labels", "", false)
  125. func splitMaybeSubscriptedPath(fieldPath string) (string, string, bool) {
  126. if !strings.HasSuffix(fieldPath, "']") {
  127. return fieldPath, "", false
  128. }
  129. s := strings.TrimSuffix(fieldPath, "']")
  130. parts := strings.SplitN(s, "['", 2)
  131. if len(parts) < 2 {
  132. return fieldPath, "", false
  133. }
  134. if len(parts[0]) == 0 {
  135. return fieldPath, "", false
  136. }
  137. return parts[0], parts[1], true
  138. }
  139. // formatMap formats map[string]string to a string.
  140. func formatMap(m map[string]string) (fmtStr string) {
  141. // output with keys in sorted order to provide stable output
  142. keys := sets.NewString()
  143. for key := range m {
  144. keys.Insert(key)
  145. }
  146. for _, key := range keys.List() {
  147. fmtStr += fmt.Sprintf("%v=%q\n", key, m[key])
  148. }
  149. fmtStr = strings.TrimSuffix(fmtStr, "\n")
  150. return
  151. }
  152. // getResourceFieldRef returns the value of a resource in the given container
  153. func getResourceFieldRef(from *corev1.EnvVarSource, container *corev1.Container) (string, error) {
  154. return extractContainerResourceValue(from.ResourceFieldRef, container)
  155. }
  156. // ExtractContainerResourceValue extracts the value of a resource
  157. // in an already known container
  158. func extractContainerResourceValue(fs *corev1.ResourceFieldSelector, container *corev1.Container) (string, error) {
  159. divisor := resource.Quantity{}
  160. if divisor.Cmp(fs.Divisor) == 0 {
  161. divisor = resource.MustParse("1")
  162. } else {
  163. divisor = fs.Divisor
  164. }
  165. switch fs.Resource {
  166. case "limits.cpu":
  167. return convertResourceCPUToString(container.Resources.Limits.Cpu(), divisor)
  168. case "limits.memory":
  169. return convertResourceMemoryToString(container.Resources.Limits.Memory(), divisor)
  170. case "limits.ephemeral-storage":
  171. return convertResourceEphemeralStorageToString(container.Resources.Limits.StorageEphemeral(), divisor)
  172. case "requests.cpu":
  173. return convertResourceCPUToString(container.Resources.Requests.Cpu(), divisor)
  174. case "requests.memory":
  175. return convertResourceMemoryToString(container.Resources.Requests.Memory(), divisor)
  176. case "requests.ephemeral-storage":
  177. return convertResourceEphemeralStorageToString(container.Resources.Requests.StorageEphemeral(), divisor)
  178. }
  179. return "", fmt.Errorf("Unsupported container resource : %v", fs.Resource)
  180. }
  181. // convertResourceCPUToString converts cpu value to the format of divisor and returns
  182. // ceiling of the value.
  183. func convertResourceCPUToString(cpu *resource.Quantity, divisor resource.Quantity) (string, error) {
  184. c := int64(math.Ceil(float64(cpu.MilliValue()) / float64(divisor.MilliValue())))
  185. return strconv.FormatInt(c, 10), nil
  186. }
  187. // convertResourceMemoryToString converts memory value to the format of divisor and returns
  188. // ceiling of the value.
  189. func convertResourceMemoryToString(memory *resource.Quantity, divisor resource.Quantity) (string, error) {
  190. m := int64(math.Ceil(float64(memory.Value()) / float64(divisor.Value())))
  191. return strconv.FormatInt(m, 10), nil
  192. }
  193. // convertResourceEphemeralStorageToString converts ephemeral storage value to the format of divisor and returns
  194. // ceiling of the value.
  195. func convertResourceEphemeralStorageToString(ephemeralStorage *resource.Quantity, divisor resource.Quantity) (string, error) {
  196. m := int64(math.Ceil(float64(ephemeralStorage.Value()) / float64(divisor.Value())))
  197. return strconv.FormatInt(m, 10), nil
  198. }
  199. // GetEnvVarRefValue returns the value referenced by the supplied EnvVarSource given the other supplied information.
  200. func GetEnvVarRefValue(kc kubernetes.Interface, ns string, store *ResourceStore, from *corev1.EnvVarSource, obj runtime.Object, c *corev1.Container) (string, error) {
  201. if from.SecretKeyRef != nil {
  202. return getSecretRefValue(kc, ns, store, from.SecretKeyRef)
  203. }
  204. if from.ConfigMapKeyRef != nil {
  205. return getConfigMapRefValue(kc, ns, store, from.ConfigMapKeyRef)
  206. }
  207. if from.FieldRef != nil {
  208. return getFieldRef(obj, from)
  209. }
  210. if from.ResourceFieldRef != nil {
  211. return getResourceFieldRef(from, c)
  212. }
  213. return "", fmt.Errorf("invalid valueFrom")
  214. }
  215. // GetEnvVarRefString returns a text description of whichever field is set within the supplied EnvVarSource argument.
  216. func GetEnvVarRefString(from *corev1.EnvVarSource) string {
  217. if from.ConfigMapKeyRef != nil {
  218. return fmt.Sprintf("configmap %s, key %s", from.ConfigMapKeyRef.Name, from.ConfigMapKeyRef.Key)
  219. }
  220. if from.SecretKeyRef != nil {
  221. return fmt.Sprintf("secret %s, key %s", from.SecretKeyRef.Name, from.SecretKeyRef.Key)
  222. }
  223. if from.FieldRef != nil {
  224. return fmt.Sprintf("field path %s", from.FieldRef.FieldPath)
  225. }
  226. if from.ResourceFieldRef != nil {
  227. containerPrefix := ""
  228. if from.ResourceFieldRef.ContainerName != "" {
  229. containerPrefix = fmt.Sprintf("%s/", from.ResourceFieldRef.ContainerName)
  230. }
  231. return fmt.Sprintf("resource field %s%s", containerPrefix, from.ResourceFieldRef.Resource)
  232. }
  233. return "invalid valueFrom"
  234. }