init_dryrun.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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 apiclient
  14. import (
  15. "net"
  16. "strings"
  17. "github.com/pkg/errors"
  18. "k8s.io/api/core/v1"
  19. apierrors "k8s.io/apimachinery/pkg/api/errors"
  20. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  21. "k8s.io/apimachinery/pkg/runtime"
  22. "k8s.io/apimachinery/pkg/util/intstr"
  23. core "k8s.io/client-go/testing"
  24. "k8s.io/kubernetes/cmd/kubeadm/app/constants"
  25. "k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
  26. )
  27. // InitDryRunGetter implements the DryRunGetter interface and can be used to GET/LIST values in the dryrun fake clientset
  28. // Need to handle these routes in a special manner:
  29. // - GET /default/services/kubernetes -- must return a valid Service
  30. // - GET /clusterrolebindings/system:nodes -- can safely return a NotFound error
  31. // - GET /kube-system/secrets/bootstrap-token-* -- can safely return a NotFound error
  32. // - GET /nodes/<node-name> -- must return a valid Node
  33. // - ...all other, unknown GETs/LISTs will be logged
  34. type InitDryRunGetter struct {
  35. controlPlaneName string
  36. serviceSubnet string
  37. }
  38. // InitDryRunGetter should implement the DryRunGetter interface
  39. var _ DryRunGetter = &InitDryRunGetter{}
  40. // NewInitDryRunGetter creates a new instance of the InitDryRunGetter struct
  41. func NewInitDryRunGetter(controlPlaneName string, serviceSubnet string) *InitDryRunGetter {
  42. return &InitDryRunGetter{
  43. controlPlaneName: controlPlaneName,
  44. serviceSubnet: serviceSubnet,
  45. }
  46. }
  47. // HandleGetAction handles GET actions to the dryrun clientset this interface supports
  48. func (idr *InitDryRunGetter) HandleGetAction(action core.GetAction) (bool, runtime.Object, error) {
  49. funcs := []func(core.GetAction) (bool, runtime.Object, error){
  50. idr.handleKubernetesService,
  51. idr.handleGetNode,
  52. idr.handleSystemNodesClusterRoleBinding,
  53. idr.handleGetBootstrapToken,
  54. }
  55. for _, f := range funcs {
  56. handled, obj, err := f(action)
  57. if handled {
  58. return handled, obj, err
  59. }
  60. }
  61. return false, nil, nil
  62. }
  63. // HandleListAction handles GET actions to the dryrun clientset this interface supports.
  64. // Currently there are no known LIST calls during kubeadm init this code has to take care of.
  65. func (idr *InitDryRunGetter) HandleListAction(action core.ListAction) (bool, runtime.Object, error) {
  66. return false, nil, nil
  67. }
  68. // handleKubernetesService returns a faked Kubernetes service in order to be able to continue running kubeadm init.
  69. // The kube-dns addon code GETs the Kubernetes service in order to extract the service subnet
  70. func (idr *InitDryRunGetter) handleKubernetesService(action core.GetAction) (bool, runtime.Object, error) {
  71. if action.GetName() != "kubernetes" || action.GetNamespace() != metav1.NamespaceDefault || action.GetResource().Resource != "services" {
  72. // We can't handle this event
  73. return false, nil, nil
  74. }
  75. _, svcSubnet, err := net.ParseCIDR(idr.serviceSubnet)
  76. if err != nil {
  77. return true, nil, errors.Wrapf(err, "error parsing CIDR %q", idr.serviceSubnet)
  78. }
  79. internalAPIServerVirtualIP, err := ipallocator.GetIndexedIP(svcSubnet, 1)
  80. if err != nil {
  81. return true, nil, errors.Wrapf(err, "unable to get first IP address from the given CIDR (%s)", svcSubnet.String())
  82. }
  83. // The only used field of this Service object is the ClusterIP, which kube-dns uses to calculate its own IP
  84. return true, &v1.Service{
  85. ObjectMeta: metav1.ObjectMeta{
  86. Name: "kubernetes",
  87. Namespace: metav1.NamespaceDefault,
  88. Labels: map[string]string{
  89. "component": "apiserver",
  90. "provider": "kubernetes",
  91. },
  92. },
  93. Spec: v1.ServiceSpec{
  94. ClusterIP: internalAPIServerVirtualIP.String(),
  95. Ports: []v1.ServicePort{
  96. {
  97. Name: "https",
  98. Port: 443,
  99. TargetPort: intstr.FromInt(6443),
  100. },
  101. },
  102. },
  103. }, nil
  104. }
  105. // handleGetNode returns a fake node object for the purpose of moving kubeadm init forwards.
  106. func (idr *InitDryRunGetter) handleGetNode(action core.GetAction) (bool, runtime.Object, error) {
  107. if action.GetName() != idr.controlPlaneName || action.GetResource().Resource != "nodes" {
  108. // We can't handle this event
  109. return false, nil, nil
  110. }
  111. return true, &v1.Node{
  112. ObjectMeta: metav1.ObjectMeta{
  113. Name: idr.controlPlaneName,
  114. Labels: map[string]string{
  115. "kubernetes.io/hostname": idr.controlPlaneName,
  116. },
  117. Annotations: map[string]string{},
  118. },
  119. }, nil
  120. }
  121. // handleSystemNodesClusterRoleBinding handles the GET call to the system:nodes clusterrolebinding
  122. func (idr *InitDryRunGetter) handleSystemNodesClusterRoleBinding(action core.GetAction) (bool, runtime.Object, error) {
  123. if action.GetName() != constants.NodesClusterRoleBinding || action.GetResource().Resource != "clusterrolebindings" {
  124. // We can't handle this event
  125. return false, nil, nil
  126. }
  127. // We can safely return a NotFound error here as the code will just proceed normally and don't care about modifying this clusterrolebinding
  128. // This can only happen on an upgrade; and in that case the ClientBackedDryRunGetter impl will be used
  129. return true, nil, apierrors.NewNotFound(action.GetResource().GroupResource(), "clusterrolebinding not found")
  130. }
  131. // handleGetBootstrapToken handles the case where kubeadm init creates the default token; and the token code GETs the
  132. // bootstrap token secret first in order to check if it already exists
  133. func (idr *InitDryRunGetter) handleGetBootstrapToken(action core.GetAction) (bool, runtime.Object, error) {
  134. if !strings.HasPrefix(action.GetName(), "bootstrap-token-") || action.GetNamespace() != metav1.NamespaceSystem || action.GetResource().Resource != "secrets" {
  135. // We can't handle this event
  136. return false, nil, nil
  137. }
  138. // We can safely return a NotFound error here as the code will just proceed normally and create the Bootstrap Token
  139. return true, nil, apierrors.NewNotFound(action.GetResource().GroupResource(), "secret not found")
  140. }