podspec_mutation.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  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 selfhosting
  14. import (
  15. "path/filepath"
  16. "strings"
  17. v1 "k8s.io/api/core/v1"
  18. kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
  19. kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
  20. )
  21. const (
  22. // selfHostedKubeConfigDir sets the directory where kubeconfig files for the scheduler and controller-manager should be mounted
  23. // Due to how the projected volume mount works (can only be a full directory, not mount individual files), we must change this from
  24. // the default as mounts cannot be nested (/etc/kubernetes would override /etc/kubernetes/pki)
  25. selfHostedKubeConfigDir = "/etc/kubernetes/kubeconfig"
  26. )
  27. // PodSpecMutatorFunc is a function capable of mutating a PodSpec
  28. type PodSpecMutatorFunc func(*v1.PodSpec)
  29. // GetDefaultMutators gets the mutator functions that always should be used
  30. func GetDefaultMutators() map[string][]PodSpecMutatorFunc {
  31. return map[string][]PodSpecMutatorFunc{
  32. kubeadmconstants.KubeAPIServer: {
  33. addNodeSelectorToPodSpec,
  34. setControlPlaneTolerationOnPodSpec,
  35. setRightDNSPolicyOnPodSpec,
  36. setHostIPOnPodSpec,
  37. },
  38. kubeadmconstants.KubeControllerManager: {
  39. addNodeSelectorToPodSpec,
  40. setControlPlaneTolerationOnPodSpec,
  41. setRightDNSPolicyOnPodSpec,
  42. },
  43. kubeadmconstants.KubeScheduler: {
  44. addNodeSelectorToPodSpec,
  45. setControlPlaneTolerationOnPodSpec,
  46. setRightDNSPolicyOnPodSpec,
  47. },
  48. }
  49. }
  50. // GetMutatorsFromFeatureGates returns all mutators needed based on the feature gates passed
  51. func GetMutatorsFromFeatureGates(certsInSecrets bool) map[string][]PodSpecMutatorFunc {
  52. // Here the map of different mutators to use for the control plane's podspec is stored
  53. mutators := GetDefaultMutators()
  54. if certsInSecrets {
  55. // Some extra work to be done if we should store the control plane certificates in Secrets
  56. // Add the store-certs-in-secrets-specific mutators here so that the self-hosted component starts using them
  57. mutators[kubeadmconstants.KubeAPIServer] = append(mutators[kubeadmconstants.KubeAPIServer], setSelfHostedVolumesForAPIServer)
  58. mutators[kubeadmconstants.KubeControllerManager] = append(mutators[kubeadmconstants.KubeControllerManager], setSelfHostedVolumesForControllerManager)
  59. mutators[kubeadmconstants.KubeScheduler] = append(mutators[kubeadmconstants.KubeScheduler], setSelfHostedVolumesForScheduler)
  60. }
  61. return mutators
  62. }
  63. // mutatePodSpec makes a Static Pod-hosted PodSpec suitable for self-hosting
  64. func mutatePodSpec(mutators map[string][]PodSpecMutatorFunc, name string, podSpec *v1.PodSpec) {
  65. // Get the mutator functions for the component in question, then loop through and execute them
  66. mutatorsForComponent := mutators[name]
  67. for _, mutateFunc := range mutatorsForComponent {
  68. mutateFunc(podSpec)
  69. }
  70. }
  71. // addNodeSelectorToPodSpec makes Pod require to be scheduled on a node marked with the control-plane label
  72. func addNodeSelectorToPodSpec(podSpec *v1.PodSpec) {
  73. if podSpec.NodeSelector == nil {
  74. podSpec.NodeSelector = map[string]string{kubeadmconstants.LabelNodeRoleMaster: ""}
  75. return
  76. }
  77. podSpec.NodeSelector[kubeadmconstants.LabelNodeRoleMaster] = ""
  78. }
  79. // setControlPlaneTolerationOnPodSpec makes the Pod tolerate the control-plane taint
  80. func setControlPlaneTolerationOnPodSpec(podSpec *v1.PodSpec) {
  81. if podSpec.Tolerations == nil {
  82. podSpec.Tolerations = []v1.Toleration{kubeadmconstants.ControlPlaneToleration}
  83. return
  84. }
  85. podSpec.Tolerations = append(podSpec.Tolerations, kubeadmconstants.ControlPlaneToleration)
  86. }
  87. // setHostIPOnPodSpec sets the environment variable HOST_IP using downward API
  88. func setHostIPOnPodSpec(podSpec *v1.PodSpec) {
  89. envVar := v1.EnvVar{
  90. Name: "HOST_IP",
  91. ValueFrom: &v1.EnvVarSource{
  92. FieldRef: &v1.ObjectFieldSelector{
  93. FieldPath: "status.hostIP",
  94. },
  95. },
  96. }
  97. podSpec.Containers[0].Env = append(podSpec.Containers[0].Env, envVar)
  98. for i := range podSpec.Containers[0].Command {
  99. if strings.Contains(podSpec.Containers[0].Command[i], "advertise-address") {
  100. podSpec.Containers[0].Command[i] = "--advertise-address=$(HOST_IP)"
  101. }
  102. }
  103. }
  104. // setRightDNSPolicyOnPodSpec makes sure the self-hosted components can look up things via kube-dns if necessary
  105. func setRightDNSPolicyOnPodSpec(podSpec *v1.PodSpec) {
  106. podSpec.DNSPolicy = v1.DNSClusterFirstWithHostNet
  107. }
  108. // setSelfHostedVolumesForAPIServer makes sure the self-hosted api server has the right volume source coming from a self-hosted cluster
  109. func setSelfHostedVolumesForAPIServer(podSpec *v1.PodSpec) {
  110. for i, v := range podSpec.Volumes {
  111. // If the volume name matches the expected one; switch the volume source from hostPath to cluster-hosted
  112. if v.Name == kubeadmconstants.KubeCertificatesVolumeName {
  113. podSpec.Volumes[i].VolumeSource = apiServerCertificatesVolumeSource()
  114. }
  115. }
  116. }
  117. // setSelfHostedVolumesForControllerManager makes sure the self-hosted controller manager has the right volume source coming from a self-hosted cluster
  118. func setSelfHostedVolumesForControllerManager(podSpec *v1.PodSpec) {
  119. for i, v := range podSpec.Volumes {
  120. // If the volume name matches the expected one; switch the volume source from hostPath to cluster-hosted
  121. if v.Name == kubeadmconstants.KubeCertificatesVolumeName {
  122. podSpec.Volumes[i].VolumeSource = controllerManagerCertificatesVolumeSource()
  123. } else if v.Name == kubeadmconstants.KubeConfigVolumeName {
  124. podSpec.Volumes[i].VolumeSource = kubeConfigVolumeSource(kubeadmconstants.ControllerManagerKubeConfigFileName)
  125. }
  126. }
  127. // Change directory for the kubeconfig directory to selfHostedKubeConfigDir
  128. for i, vm := range podSpec.Containers[0].VolumeMounts {
  129. if vm.Name == kubeadmconstants.KubeConfigVolumeName {
  130. podSpec.Containers[0].VolumeMounts[i].MountPath = selfHostedKubeConfigDir
  131. }
  132. }
  133. // Rewrite the --kubeconfig path as the volume mount path may not overlap with certs dir, which it does by default (/etc/kubernetes and /etc/kubernetes/pki)
  134. // This is not a problem with hostPath mounts as hostPath supports mounting one file only, instead of always a full directory. Secrets and Projected Volumes
  135. // don't support that.
  136. podSpec.Containers[0].Command = kubeadmutil.ReplaceArgument(podSpec.Containers[0].Command, func(argMap map[string]string) map[string]string {
  137. controllerManagerKubeConfigPath := filepath.Join(selfHostedKubeConfigDir, kubeadmconstants.ControllerManagerKubeConfigFileName)
  138. argMap["kubeconfig"] = controllerManagerKubeConfigPath
  139. if _, ok := argMap["authentication-kubeconfig"]; ok {
  140. argMap["authentication-kubeconfig"] = controllerManagerKubeConfigPath
  141. }
  142. if _, ok := argMap["authorization-kubeconfig"]; ok {
  143. argMap["authorization-kubeconfig"] = controllerManagerKubeConfigPath
  144. }
  145. return argMap
  146. })
  147. }
  148. // setSelfHostedVolumesForScheduler makes sure the self-hosted scheduler has the right volume source coming from a self-hosted cluster
  149. func setSelfHostedVolumesForScheduler(podSpec *v1.PodSpec) {
  150. for i, v := range podSpec.Volumes {
  151. // If the volume name matches the expected one; switch the volume source from hostPath to cluster-hosted
  152. if v.Name == kubeadmconstants.KubeConfigVolumeName {
  153. podSpec.Volumes[i].VolumeSource = kubeConfigVolumeSource(kubeadmconstants.SchedulerKubeConfigFileName)
  154. }
  155. }
  156. // Change directory for the kubeconfig directory to selfHostedKubeConfigDir
  157. for i, vm := range podSpec.Containers[0].VolumeMounts {
  158. if vm.Name == kubeadmconstants.KubeConfigVolumeName {
  159. podSpec.Containers[0].VolumeMounts[i].MountPath = selfHostedKubeConfigDir
  160. }
  161. }
  162. // Rewrite the --kubeconfig path as the volume mount path may not overlap with certs dir, which it does by default (/etc/kubernetes and /etc/kubernetes/pki)
  163. // This is not a problem with hostPath mounts as hostPath supports mounting one file only, instead of always a full directory. Secrets and Projected Volumes
  164. // don't support that.
  165. podSpec.Containers[0].Command = kubeadmutil.ReplaceArgument(podSpec.Containers[0].Command, func(argMap map[string]string) map[string]string {
  166. schedulerKubeConfigPath := filepath.Join(selfHostedKubeConfigDir, kubeadmconstants.SchedulerKubeConfigFileName)
  167. argMap["kubeconfig"] = schedulerKubeConfigPath
  168. if _, ok := argMap["authentication-kubeconfig"]; ok {
  169. argMap["authentication-kubeconfig"] = schedulerKubeConfigPath
  170. }
  171. if _, ok := argMap["authorization-kubeconfig"]; ok {
  172. argMap["authorization-kubeconfig"] = schedulerKubeConfigPath
  173. }
  174. return argMap
  175. })
  176. }