github.phpd.cn/cilium/cilium@v1.6.12/pkg/k8s/init.go (about)

     1  // Copyright 2016-2019 Authors of Cilium
     2  //
     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  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package k8s abstracts all Kubernetes specific behaviour
    16  package k8s
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"os"
    22  	"time"
    23  
    24  	"github.com/cilium/cilium/pkg/backoff"
    25  	cilium_v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
    26  	"github.com/cilium/cilium/pkg/k8s/types"
    27  	k8sversion "github.com/cilium/cilium/pkg/k8s/version"
    28  	"github.com/cilium/cilium/pkg/logging/logfields"
    29  	"github.com/cilium/cilium/pkg/node"
    30  	"github.com/cilium/cilium/pkg/option"
    31  	"github.com/cilium/cilium/pkg/source"
    32  
    33  	"github.com/sirupsen/logrus"
    34  	apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
    35  )
    36  
    37  const (
    38  	nodeRetrievalMaxRetries = 15
    39  )
    40  
    41  func waitForNodeInformation(nodeName string) *node.Node {
    42  	backoff := backoff.Exponential{
    43  		Min:    time.Duration(200) * time.Millisecond,
    44  		Factor: 2.0,
    45  		Name:   "k8s-node-retrieval",
    46  	}
    47  
    48  	for retry := 0; retry < nodeRetrievalMaxRetries; retry++ {
    49  		n, err := retrieveNodeInformation(nodeName)
    50  		if err != nil {
    51  			log.WithError(err).Warning("Waiting for k8s node information")
    52  			backoff.Wait(context.TODO())
    53  			continue
    54  		}
    55  
    56  		return n
    57  	}
    58  
    59  	return nil
    60  }
    61  
    62  func retrieveNodeInformation(nodeName string) (*node.Node, error) {
    63  	requireIPv4CIDR := option.Config.K8sRequireIPv4PodCIDR
    64  	requireIPv6CIDR := option.Config.K8sRequireIPv6PodCIDR
    65  
    66  	k8sNode, err := GetNode(Client(), nodeName)
    67  	if err != nil {
    68  		// If no CIDR is required, retrieving the node information is
    69  		// optional
    70  		if !requireIPv4CIDR && !requireIPv6CIDR {
    71  			return nil, nil
    72  		}
    73  
    74  		return nil, fmt.Errorf("unable to retrieve k8s node information: %s", err)
    75  
    76  	}
    77  
    78  	nodeInterface := ConvertToNode(k8sNode)
    79  	if nodeInterface == nil {
    80  		// This will never happen and the GetNode on line 63 will be soon
    81  		// make a request from the local store instead.
    82  		return nil, fmt.Errorf("invalid k8s node: %s", k8sNode)
    83  	}
    84  	typesNode := nodeInterface.(*types.Node)
    85  
    86  	// The source is left unspecified as this node resource should never be
    87  	// used to update state
    88  	n := ParseNode(typesNode, source.Unspec)
    89  	log.WithField(logfields.NodeName, n.Name).Info("Retrieved node information from kubernetes")
    90  
    91  	if requireIPv4CIDR && n.IPv4AllocCIDR == nil {
    92  		return nil, fmt.Errorf("required IPv4 pod CIDR not present in node resource")
    93  	}
    94  
    95  	if requireIPv6CIDR && n.IPv6AllocCIDR == nil {
    96  		return nil, fmt.Errorf("required IPv6 pod CIDR not present in node resource")
    97  	}
    98  
    99  	return n, nil
   100  }
   101  
   102  // useNodeCIDR sets the ipv4-range and ipv6-range values values from the
   103  // addresses defined in the given node.
   104  func useNodeCIDR(n *node.Node) {
   105  	if n.IPv4AllocCIDR != nil && option.Config.EnableIPv4 {
   106  		node.SetIPv4AllocRange(n.IPv4AllocCIDR)
   107  	}
   108  	if n.IPv6AllocCIDR != nil && option.Config.EnableIPv6 {
   109  		if err := node.SetIPv6NodeRange(n.IPv6AllocCIDR.IPNet); err != nil {
   110  			log.WithError(err).WithFields(logrus.Fields{
   111  				logfields.Node:     n.Name,
   112  				logfields.V6Prefix: n.IPv6AllocCIDR,
   113  			}).Warn("k8s: Can't use IPv6 CIDR range from k8s")
   114  		}
   115  	}
   116  }
   117  
   118  // Init initializes the Kubernetes package. It is required to call Configure()
   119  // beforehand.
   120  func Init() error {
   121  	if err := createDefaultClient(); err != nil {
   122  		return fmt.Errorf("unable to create k8s client: %s", err)
   123  	}
   124  
   125  	if err := createDefaultCiliumClient(); err != nil {
   126  		return fmt.Errorf("unable to create cilium k8s client: %s", err)
   127  	}
   128  
   129  	if err := k8sversion.Update(Client()); err != nil {
   130  		return err
   131  	}
   132  
   133  	if !k8sversion.Capabilities().MinimalVersionMet {
   134  		return fmt.Errorf("k8s version (%v) is not meeting the minimal requirement (%v)",
   135  			k8sversion.Version(), k8sversion.MinimalVersionConstraint)
   136  	}
   137  
   138  	if nodeName := os.Getenv(EnvNodeNameSpec); nodeName != "" {
   139  		// Use of the environment variable overwrites the node-name
   140  		// automatically derived
   141  		node.SetName(nodeName)
   142  
   143  		if n := waitForNodeInformation(nodeName); n != nil {
   144  			nodeIP4 := n.GetNodeIP(false)
   145  			nodeIP6 := n.GetNodeIP(true)
   146  
   147  			log.WithFields(logrus.Fields{
   148  				logfields.NodeName:         n.Name,
   149  				logfields.IPAddr + ".ipv4": nodeIP4,
   150  				logfields.IPAddr + ".ipv6": nodeIP6,
   151  				logfields.V4Prefix:         n.IPv4AllocCIDR,
   152  				logfields.V6Prefix:         n.IPv6AllocCIDR,
   153  			}).Info("Received own node information from API server")
   154  
   155  			useNodeCIDR(n)
   156  
   157  			// Note: Node IPs are derived regardless of
   158  			// option.Config.EnableIPv4 and
   159  			// option.Config.EnableIPv6. This is done to enable
   160  			// underlay addressing to be different from overlay
   161  			// addressing, e.g. an IPv6 only PodCIDR running over
   162  			// IPv4 encapsulation.
   163  			if nodeIP4 != nil {
   164  				node.SetExternalIPv4(nodeIP4)
   165  			}
   166  
   167  			if nodeIP6 != nil {
   168  				node.SetIPv6(nodeIP6)
   169  			}
   170  		} else {
   171  			// if node resource could not be received, fail if
   172  			// PodCIDR requirement has been requested
   173  			if option.Config.K8sRequireIPv4PodCIDR || option.Config.K8sRequireIPv6PodCIDR {
   174  				log.Fatal("Unable to derive PodCIDR from Kubernetes node resource, giving up")
   175  			}
   176  		}
   177  
   178  		// Annotate addresses will occur later since the user might
   179  		// want to specify them manually
   180  	} else if option.Config.K8sRequireIPv4PodCIDR || option.Config.K8sRequireIPv6PodCIDR {
   181  		return fmt.Errorf("node name must be specified via environment variable '%s' to retrieve Kubernetes PodCIDR range", EnvNodeNameSpec)
   182  	}
   183  
   184  	return nil
   185  }
   186  
   187  // RegisterCRDs registers all CRDs
   188  func RegisterCRDs() error {
   189  	if option.Config.SkipCRDCreation {
   190  		return nil
   191  	}
   192  
   193  	restConfig, err := CreateConfig()
   194  	if err != nil {
   195  		return fmt.Errorf("Unable to create rest configuration: %s", err)
   196  	}
   197  
   198  	apiextensionsclientset, err := apiextensionsclient.NewForConfig(restConfig)
   199  	if err != nil {
   200  		return fmt.Errorf("Unable to create rest configuration for k8s CRD: %s", err)
   201  	}
   202  
   203  	err = cilium_v2.CreateCustomResourceDefinitions(apiextensionsclientset)
   204  	if err != nil {
   205  		return fmt.Errorf("Unable to create custom resource definition: %s", err)
   206  	}
   207  
   208  	return nil
   209  }