kubelet_node_status.go 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836
  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 kubelet
  14. import (
  15. "fmt"
  16. "math"
  17. "net"
  18. goRuntime "runtime"
  19. "sort"
  20. "strings"
  21. "time"
  22. "github.com/golang/glog"
  23. "k8s.io/kubernetes/pkg/api"
  24. apierrors "k8s.io/kubernetes/pkg/api/errors"
  25. "k8s.io/kubernetes/pkg/api/resource"
  26. "k8s.io/kubernetes/pkg/api/unversioned"
  27. "k8s.io/kubernetes/pkg/cloudprovider"
  28. "k8s.io/kubernetes/pkg/kubelet/cadvisor"
  29. "k8s.io/kubernetes/pkg/kubelet/events"
  30. "k8s.io/kubernetes/pkg/kubelet/util/sliceutils"
  31. utilnet "k8s.io/kubernetes/pkg/util/net"
  32. "k8s.io/kubernetes/pkg/version"
  33. "k8s.io/kubernetes/pkg/volume/util/volumehelper"
  34. )
  35. // registerWithApiServer registers the node with the cluster master. It is safe
  36. // to call multiple times, but not concurrently (kl.registrationCompleted is
  37. // not locked).
  38. func (kl *Kubelet) registerWithApiServer() {
  39. if kl.registrationCompleted {
  40. return
  41. }
  42. step := 100 * time.Millisecond
  43. for {
  44. time.Sleep(step)
  45. step = step * 2
  46. if step >= 7*time.Second {
  47. step = 7 * time.Second
  48. }
  49. node, err := kl.initialNode()
  50. if err != nil {
  51. glog.Errorf("Unable to construct api.Node object for kubelet: %v", err)
  52. continue
  53. }
  54. glog.Infof("Attempting to register node %s", node.Name)
  55. registered := kl.tryRegisterWithApiServer(node)
  56. if registered {
  57. glog.Infof("Successfully registered node %s", node.Name)
  58. kl.registrationCompleted = true
  59. return
  60. }
  61. }
  62. }
  63. // tryRegisterWithApiServer makes an attempt to register the given node with
  64. // the API server, returning a boolean indicating whether the attempt was
  65. // successful. If a node with the same name already exists, it reconciles the
  66. // value of the annotation for controller-managed attach-detach of attachable
  67. // persistent volumes for the node. If a node of the same name exists but has
  68. // a different externalID value, it attempts to delete that node so that a
  69. // later attempt can recreate it.
  70. func (kl *Kubelet) tryRegisterWithApiServer(node *api.Node) bool {
  71. _, err := kl.kubeClient.Core().Nodes().Create(node)
  72. if err == nil {
  73. return true
  74. }
  75. if !apierrors.IsAlreadyExists(err) {
  76. glog.Errorf("Unable to register node %q with API server: %v", kl.nodeName, err)
  77. return false
  78. }
  79. existingNode, err := kl.kubeClient.Core().Nodes().Get(kl.nodeName)
  80. if err != nil {
  81. glog.Errorf("Unable to register node %q with API server: error getting existing node: %v", kl.nodeName, err)
  82. return false
  83. }
  84. if existingNode == nil {
  85. glog.Errorf("Unable to register node %q with API server: no node instance returned", kl.nodeName)
  86. return false
  87. }
  88. if existingNode.Spec.ExternalID == node.Spec.ExternalID {
  89. glog.Infof("Node %s was previously registered", kl.nodeName)
  90. // Edge case: the node was previously registered; reconcile
  91. // the value of the controller-managed attach-detach
  92. // annotation.
  93. requiresUpdate := kl.reconcileCMADAnnotationWithExistingNode(node, existingNode)
  94. if requiresUpdate {
  95. if _, err := kl.kubeClient.Core().Nodes().Update(existingNode); err != nil {
  96. glog.Errorf("Unable to reconcile node %q with API server: error updating node: %v", kl.nodeName, err)
  97. return false
  98. }
  99. }
  100. return true
  101. }
  102. glog.Errorf(
  103. "Previously node %q had externalID %q; now it is %q; will delete and recreate.",
  104. kl.nodeName, node.Spec.ExternalID, existingNode.Spec.ExternalID,
  105. )
  106. if err := kl.kubeClient.Core().Nodes().Delete(node.Name, nil); err != nil {
  107. glog.Errorf("Unable to register node %q with API server: error deleting old node: %v", kl.nodeName, err)
  108. } else {
  109. glog.Info("Deleted old node object %q", kl.nodeName)
  110. }
  111. return false
  112. }
  113. // reconcileCMADAnnotationWithExistingNode reconciles the controller-managed
  114. // attach-detach annotation on a new node and the existing node, returning
  115. // whether the existing node must be updated.
  116. func (kl *Kubelet) reconcileCMADAnnotationWithExistingNode(node, existingNode *api.Node) bool {
  117. var (
  118. existingCMAAnnotation = existingNode.Annotations[volumehelper.ControllerManagedAttachAnnotation]
  119. newCMAAnnotation, newSet = node.Annotations[volumehelper.ControllerManagedAttachAnnotation]
  120. )
  121. if newCMAAnnotation == existingCMAAnnotation {
  122. return false
  123. }
  124. // If the just-constructed node and the existing node do
  125. // not have the same value, update the existing node with
  126. // the correct value of the annotation.
  127. if !newSet {
  128. glog.Info("Controller attach-detach setting changed to false; updating existing Node")
  129. delete(existingNode.Annotations, volumehelper.ControllerManagedAttachAnnotation)
  130. } else {
  131. glog.Info("Controller attach-detach setting changed to true; updating existing Node")
  132. if existingNode.Annotations == nil {
  133. existingNode.Annotations = make(map[string]string)
  134. }
  135. existingNode.Annotations[volumehelper.ControllerManagedAttachAnnotation] = newCMAAnnotation
  136. }
  137. return true
  138. }
  139. // initialNode constructs the initial api.Node for this Kubelet, incorporating node
  140. // labels, information from the cloud provider, and Kubelet configuration.
  141. func (kl *Kubelet) initialNode() (*api.Node, error) {
  142. node := &api.Node{
  143. ObjectMeta: api.ObjectMeta{
  144. Name: kl.nodeName,
  145. Labels: map[string]string{
  146. unversioned.LabelHostname: kl.hostname,
  147. unversioned.LabelOS: goRuntime.GOOS,
  148. unversioned.LabelArch: goRuntime.GOARCH,
  149. },
  150. },
  151. Spec: api.NodeSpec{
  152. Unschedulable: !kl.registerSchedulable,
  153. },
  154. }
  155. // Initially, set NodeNetworkUnavailable to true.
  156. if kl.providerRequiresNetworkingConfiguration() {
  157. node.Status.Conditions = append(node.Status.Conditions, api.NodeCondition{
  158. Type: api.NodeNetworkUnavailable,
  159. Status: api.ConditionTrue,
  160. Reason: "NoRouteCreated",
  161. Message: "Node created without a route",
  162. LastTransitionTime: unversioned.NewTime(kl.clock.Now()),
  163. })
  164. }
  165. if kl.enableControllerAttachDetach {
  166. if node.Annotations == nil {
  167. node.Annotations = make(map[string]string)
  168. }
  169. glog.Infof("Setting node annotation to enable volume controller attach/detach")
  170. node.Annotations[volumehelper.ControllerManagedAttachAnnotation] = "true"
  171. } else {
  172. glog.Infof("Controller attach/detach is disabled for this node; Kubelet will attach and detach volumes")
  173. }
  174. // @question: should this be place after the call to the cloud provider? which also applies labels
  175. for k, v := range kl.nodeLabels {
  176. if cv, found := node.ObjectMeta.Labels[k]; found {
  177. glog.Warningf("the node label %s=%s will overwrite default setting %s", k, v, cv)
  178. }
  179. node.ObjectMeta.Labels[k] = v
  180. }
  181. if kl.cloud != nil {
  182. instances, ok := kl.cloud.Instances()
  183. if !ok {
  184. return nil, fmt.Errorf("failed to get instances from cloud provider")
  185. }
  186. // TODO(roberthbailey): Can we do this without having credentials to talk
  187. // to the cloud provider?
  188. // TODO: ExternalID is deprecated, we'll have to drop this code
  189. externalID, err := instances.ExternalID(kl.nodeName)
  190. if err != nil {
  191. return nil, fmt.Errorf("failed to get external ID from cloud provider: %v", err)
  192. }
  193. node.Spec.ExternalID = externalID
  194. // TODO: We can't assume that the node has credentials to talk to the
  195. // cloudprovider from arbitrary nodes. At most, we should talk to a
  196. // local metadata server here.
  197. node.Spec.ProviderID, err = cloudprovider.GetInstanceProviderID(kl.cloud, kl.nodeName)
  198. if err != nil {
  199. return nil, err
  200. }
  201. instanceType, err := instances.InstanceType(kl.nodeName)
  202. if err != nil {
  203. return nil, err
  204. }
  205. if instanceType != "" {
  206. glog.Infof("Adding node label from cloud provider: %s=%s", unversioned.LabelInstanceType, instanceType)
  207. node.ObjectMeta.Labels[unversioned.LabelInstanceType] = instanceType
  208. }
  209. // If the cloud has zone information, label the node with the zone information
  210. zones, ok := kl.cloud.Zones()
  211. if ok {
  212. zone, err := zones.GetZone()
  213. if err != nil {
  214. return nil, fmt.Errorf("failed to get zone from cloud provider: %v", err)
  215. }
  216. if zone.FailureDomain != "" {
  217. glog.Infof("Adding node label from cloud provider: %s=%s", unversioned.LabelZoneFailureDomain, zone.FailureDomain)
  218. node.ObjectMeta.Labels[unversioned.LabelZoneFailureDomain] = zone.FailureDomain
  219. }
  220. if zone.Region != "" {
  221. glog.Infof("Adding node label from cloud provider: %s=%s", unversioned.LabelZoneRegion, zone.Region)
  222. node.ObjectMeta.Labels[unversioned.LabelZoneRegion] = zone.Region
  223. }
  224. }
  225. } else {
  226. node.Spec.ExternalID = kl.hostname
  227. if kl.autoDetectCloudProvider {
  228. // If no cloud provider is defined - use the one detected by cadvisor
  229. info, err := kl.GetCachedMachineInfo()
  230. if err == nil {
  231. kl.updateCloudProviderFromMachineInfo(node, info)
  232. }
  233. }
  234. }
  235. if err := kl.setNodeStatus(node); err != nil {
  236. return nil, err
  237. }
  238. return node, nil
  239. }
  240. // syncNodeStatus should be called periodically from a goroutine.
  241. // It synchronizes node status to master, registering the kubelet first if
  242. // necessary.
  243. func (kl *Kubelet) syncNodeStatus() {
  244. if kl.kubeClient == nil {
  245. return
  246. }
  247. if kl.registerNode {
  248. // This will exit immediately if it doesn't need to do anything.
  249. kl.registerWithApiServer()
  250. }
  251. if err := kl.updateNodeStatus(); err != nil {
  252. glog.Errorf("Unable to update node status: %v", err)
  253. }
  254. }
  255. // updateNodeStatus updates node status to master with retries.
  256. func (kl *Kubelet) updateNodeStatus() error {
  257. for i := 0; i < nodeStatusUpdateRetry; i++ {
  258. if err := kl.tryUpdateNodeStatus(); err != nil {
  259. glog.Errorf("Error updating node status, will retry: %v", err)
  260. } else {
  261. return nil
  262. }
  263. }
  264. return fmt.Errorf("update node status exceeds retry count")
  265. }
  266. // tryUpdateNodeStatus tries to update node status to master. If ReconcileCBR0
  267. // is set, this function will also confirm that cbr0 is configured correctly.
  268. func (kl *Kubelet) tryUpdateNodeStatus() error {
  269. node, err := kl.kubeClient.Core().Nodes().Get(kl.nodeName)
  270. if err != nil {
  271. return fmt.Errorf("error getting node %q: %v", kl.nodeName, err)
  272. }
  273. if node == nil {
  274. return fmt.Errorf("no node instance returned for %q", kl.nodeName)
  275. }
  276. // Flannel is the authoritative source of pod CIDR, if it's running.
  277. // This is a short term compromise till we get flannel working in
  278. // reservation mode.
  279. if kl.flannelExperimentalOverlay {
  280. flannelPodCIDR := kl.runtimeState.podCIDR()
  281. if node.Spec.PodCIDR != flannelPodCIDR {
  282. node.Spec.PodCIDR = flannelPodCIDR
  283. glog.Infof("Updating podcidr to %v", node.Spec.PodCIDR)
  284. if updatedNode, err := kl.kubeClient.Core().Nodes().Update(node); err != nil {
  285. glog.Warningf("Failed to update podCIDR: %v", err)
  286. } else {
  287. // Update the node resourceVersion so the status update doesn't fail.
  288. node = updatedNode
  289. }
  290. }
  291. } else if kl.reconcileCIDR {
  292. kl.updatePodCIDR(node.Spec.PodCIDR)
  293. }
  294. if err := kl.setNodeStatus(node); err != nil {
  295. return err
  296. }
  297. // Update the current status on the API server
  298. updatedNode, err := kl.kubeClient.Core().Nodes().UpdateStatus(node)
  299. if err == nil {
  300. kl.volumeManager.MarkVolumesAsReportedInUse(
  301. updatedNode.Status.VolumesInUse)
  302. }
  303. return err
  304. }
  305. // recordNodeStatusEvent records an event of the given type with the given
  306. // message for the node.
  307. func (kl *Kubelet) recordNodeStatusEvent(eventtype, event string) {
  308. glog.V(2).Infof("Recording %s event message for node %s", event, kl.nodeName)
  309. // TODO: This requires a transaction, either both node status is updated
  310. // and event is recorded or neither should happen, see issue #6055.
  311. kl.recorder.Eventf(kl.nodeRef, eventtype, event, "Node %s status is now: %s", kl.nodeName, event)
  312. }
  313. // Set IP addresses for the node.
  314. func (kl *Kubelet) setNodeAddress(node *api.Node) error {
  315. if kl.cloud != nil {
  316. instances, ok := kl.cloud.Instances()
  317. if !ok {
  318. return fmt.Errorf("failed to get instances from cloud provider")
  319. }
  320. // TODO(roberthbailey): Can we do this without having credentials to talk
  321. // to the cloud provider?
  322. // TODO(justinsb): We can if CurrentNodeName() was actually CurrentNode() and returned an interface
  323. // TODO: If IP addresses couldn't be fetched from the cloud provider, should kubelet fallback on the other methods for getting the IP below?
  324. nodeAddresses, err := instances.NodeAddresses(kl.nodeName)
  325. if err != nil {
  326. return fmt.Errorf("failed to get node address from cloud provider: %v", err)
  327. }
  328. node.Status.Addresses = nodeAddresses
  329. } else {
  330. var ipAddr net.IP
  331. var err error
  332. // 1) Use nodeIP if set
  333. // 2) If the user has specified an IP to HostnameOverride, use it
  334. // 3) Lookup the IP from node name by DNS and use the first non-loopback ipv4 address
  335. // 4) Try to get the IP from the network interface used as default gateway
  336. if kl.nodeIP != nil {
  337. ipAddr = kl.nodeIP
  338. } else if addr := net.ParseIP(kl.hostname); addr != nil {
  339. ipAddr = addr
  340. } else {
  341. var addrs []net.IP
  342. addrs, err = net.LookupIP(node.Name)
  343. for _, addr := range addrs {
  344. if !addr.IsLoopback() && addr.To4() != nil {
  345. ipAddr = addr
  346. break
  347. }
  348. }
  349. if ipAddr == nil {
  350. ipAddr, err = utilnet.ChooseHostInterface()
  351. }
  352. }
  353. if ipAddr == nil {
  354. // We tried everything we could, but the IP address wasn't fetchable; error out
  355. return fmt.Errorf("can't get ip address of node %s. error: %v", node.Name, err)
  356. } else {
  357. node.Status.Addresses = []api.NodeAddress{
  358. {Type: api.NodeLegacyHostIP, Address: ipAddr.String()},
  359. {Type: api.NodeInternalIP, Address: ipAddr.String()},
  360. }
  361. }
  362. }
  363. return nil
  364. }
  365. func (kl *Kubelet) setNodeStatusMachineInfo(node *api.Node) {
  366. // TODO: Post NotReady if we cannot get MachineInfo from cAdvisor. This needs to start
  367. // cAdvisor locally, e.g. for test-cmd.sh, and in integration test.
  368. info, err := kl.GetCachedMachineInfo()
  369. if err != nil {
  370. // TODO(roberthbailey): This is required for test-cmd.sh to pass.
  371. // See if the test should be updated instead.
  372. node.Status.Capacity = api.ResourceList{
  373. api.ResourceCPU: *resource.NewMilliQuantity(0, resource.DecimalSI),
  374. api.ResourceMemory: resource.MustParse("0Gi"),
  375. api.ResourcePods: *resource.NewQuantity(int64(kl.maxPods), resource.DecimalSI),
  376. api.ResourceNvidiaGPU: *resource.NewQuantity(int64(kl.nvidiaGPUs), resource.DecimalSI),
  377. }
  378. glog.Errorf("Error getting machine info: %v", err)
  379. } else {
  380. node.Status.NodeInfo.MachineID = info.MachineID
  381. node.Status.NodeInfo.SystemUUID = info.SystemUUID
  382. node.Status.Capacity = cadvisor.CapacityFromMachineInfo(info)
  383. if kl.podsPerCore > 0 {
  384. node.Status.Capacity[api.ResourcePods] = *resource.NewQuantity(
  385. int64(math.Min(float64(info.NumCores*kl.podsPerCore), float64(kl.maxPods))), resource.DecimalSI)
  386. } else {
  387. node.Status.Capacity[api.ResourcePods] = *resource.NewQuantity(
  388. int64(kl.maxPods), resource.DecimalSI)
  389. }
  390. node.Status.Capacity[api.ResourceNvidiaGPU] = *resource.NewQuantity(
  391. int64(kl.nvidiaGPUs), resource.DecimalSI)
  392. if node.Status.NodeInfo.BootID != "" &&
  393. node.Status.NodeInfo.BootID != info.BootID {
  394. // TODO: This requires a transaction, either both node status is updated
  395. // and event is recorded or neither should happen, see issue #6055.
  396. kl.recorder.Eventf(kl.nodeRef, api.EventTypeWarning, events.NodeRebooted,
  397. "Node %s has been rebooted, boot id: %s", kl.nodeName, info.BootID)
  398. }
  399. node.Status.NodeInfo.BootID = info.BootID
  400. }
  401. // Set Allocatable.
  402. node.Status.Allocatable = make(api.ResourceList)
  403. for k, v := range node.Status.Capacity {
  404. value := *(v.Copy())
  405. if kl.reservation.System != nil {
  406. value.Sub(kl.reservation.System[k])
  407. }
  408. if kl.reservation.Kubernetes != nil {
  409. value.Sub(kl.reservation.Kubernetes[k])
  410. }
  411. if value.Sign() < 0 {
  412. // Negative Allocatable resources don't make sense.
  413. value.Set(0)
  414. }
  415. node.Status.Allocatable[k] = value
  416. }
  417. }
  418. // Set versioninfo for the node.
  419. func (kl *Kubelet) setNodeStatusVersionInfo(node *api.Node) {
  420. verinfo, err := kl.cadvisor.VersionInfo()
  421. if err != nil {
  422. glog.Errorf("Error getting version info: %v", err)
  423. } else {
  424. node.Status.NodeInfo.KernelVersion = verinfo.KernelVersion
  425. node.Status.NodeInfo.OSImage = verinfo.ContainerOsVersion
  426. runtimeVersion := "Unknown"
  427. if runtimeVer, err := kl.containerRuntime.Version(); err == nil {
  428. runtimeVersion = runtimeVer.String()
  429. }
  430. node.Status.NodeInfo.ContainerRuntimeVersion = fmt.Sprintf("%s://%s", kl.containerRuntime.Type(), runtimeVersion)
  431. node.Status.NodeInfo.KubeletVersion = version.Get().String()
  432. // TODO: kube-proxy might be different version from kubelet in the future
  433. node.Status.NodeInfo.KubeProxyVersion = version.Get().String()
  434. }
  435. }
  436. // Set daemonEndpoints for the node.
  437. func (kl *Kubelet) setNodeStatusDaemonEndpoints(node *api.Node) {
  438. node.Status.DaemonEndpoints = *kl.daemonEndpoints
  439. }
  440. // Set images list for the node
  441. func (kl *Kubelet) setNodeStatusImages(node *api.Node) {
  442. // Update image list of this node
  443. var imagesOnNode []api.ContainerImage
  444. containerImages, err := kl.imageManager.GetImageList()
  445. if err != nil {
  446. glog.Errorf("Error getting image list: %v", err)
  447. } else {
  448. // sort the images from max to min, and only set top N images into the node status.
  449. sort.Sort(sliceutils.ByImageSize(containerImages))
  450. if maxImagesInNodeStatus < len(containerImages) {
  451. containerImages = containerImages[0:maxImagesInNodeStatus]
  452. }
  453. for _, image := range containerImages {
  454. imagesOnNode = append(imagesOnNode, api.ContainerImage{
  455. Names: append(image.RepoTags, image.RepoDigests...),
  456. SizeBytes: image.Size,
  457. })
  458. }
  459. }
  460. node.Status.Images = imagesOnNode
  461. }
  462. // Set the GOOS and GOARCH for this node
  463. func (kl *Kubelet) setNodeStatusGoRuntime(node *api.Node) {
  464. node.Status.NodeInfo.OperatingSystem = goRuntime.GOOS
  465. node.Status.NodeInfo.Architecture = goRuntime.GOARCH
  466. }
  467. // Set status for the node.
  468. func (kl *Kubelet) setNodeStatusInfo(node *api.Node) {
  469. kl.setNodeStatusMachineInfo(node)
  470. kl.setNodeStatusVersionInfo(node)
  471. kl.setNodeStatusDaemonEndpoints(node)
  472. kl.setNodeStatusImages(node)
  473. kl.setNodeStatusGoRuntime(node)
  474. }
  475. // Set Ready condition for the node.
  476. func (kl *Kubelet) setNodeReadyCondition(node *api.Node) {
  477. // NOTE(aaronlevy): NodeReady condition needs to be the last in the list of node conditions.
  478. // This is due to an issue with version skewed kubelet and master components.
  479. // ref: https://github.com/kubernetes/kubernetes/issues/16961
  480. currentTime := unversioned.NewTime(kl.clock.Now())
  481. var newNodeReadyCondition api.NodeCondition
  482. if rs := kl.runtimeState.errors(); len(rs) == 0 {
  483. newNodeReadyCondition = api.NodeCondition{
  484. Type: api.NodeReady,
  485. Status: api.ConditionTrue,
  486. Reason: "KubeletReady",
  487. Message: "kubelet is posting ready status",
  488. LastHeartbeatTime: currentTime,
  489. }
  490. } else {
  491. newNodeReadyCondition = api.NodeCondition{
  492. Type: api.NodeReady,
  493. Status: api.ConditionFalse,
  494. Reason: "KubeletNotReady",
  495. Message: strings.Join(rs, ","),
  496. LastHeartbeatTime: currentTime,
  497. }
  498. }
  499. // Append AppArmor status if it's enabled.
  500. // TODO(timstclair): This is a temporary message until node feature reporting is added.
  501. if newNodeReadyCondition.Status == api.ConditionTrue &&
  502. kl.appArmorValidator != nil && kl.appArmorValidator.ValidateHost() == nil {
  503. newNodeReadyCondition.Message = fmt.Sprintf("%s. AppArmor enabled", newNodeReadyCondition.Message)
  504. }
  505. // Record any soft requirements that were not met in the container manager.
  506. status := kl.containerManager.Status()
  507. if status.SoftRequirements != nil {
  508. newNodeReadyCondition.Message = fmt.Sprintf("%s. WARNING: %s", newNodeReadyCondition.Message, status.SoftRequirements.Error())
  509. }
  510. readyConditionUpdated := false
  511. needToRecordEvent := false
  512. for i := range node.Status.Conditions {
  513. if node.Status.Conditions[i].Type == api.NodeReady {
  514. if node.Status.Conditions[i].Status == newNodeReadyCondition.Status {
  515. newNodeReadyCondition.LastTransitionTime = node.Status.Conditions[i].LastTransitionTime
  516. } else {
  517. newNodeReadyCondition.LastTransitionTime = currentTime
  518. needToRecordEvent = true
  519. }
  520. node.Status.Conditions[i] = newNodeReadyCondition
  521. readyConditionUpdated = true
  522. break
  523. }
  524. }
  525. if !readyConditionUpdated {
  526. newNodeReadyCondition.LastTransitionTime = currentTime
  527. node.Status.Conditions = append(node.Status.Conditions, newNodeReadyCondition)
  528. }
  529. if needToRecordEvent {
  530. if newNodeReadyCondition.Status == api.ConditionTrue {
  531. kl.recordNodeStatusEvent(api.EventTypeNormal, events.NodeReady)
  532. } else {
  533. kl.recordNodeStatusEvent(api.EventTypeNormal, events.NodeNotReady)
  534. }
  535. }
  536. }
  537. // setNodeMemoryPressureCondition for the node.
  538. // TODO: this needs to move somewhere centralized...
  539. func (kl *Kubelet) setNodeMemoryPressureCondition(node *api.Node) {
  540. currentTime := unversioned.NewTime(kl.clock.Now())
  541. var condition *api.NodeCondition
  542. // Check if NodeMemoryPressure condition already exists and if it does, just pick it up for update.
  543. for i := range node.Status.Conditions {
  544. if node.Status.Conditions[i].Type == api.NodeMemoryPressure {
  545. condition = &node.Status.Conditions[i]
  546. }
  547. }
  548. newCondition := false
  549. // If the NodeMemoryPressure condition doesn't exist, create one
  550. if condition == nil {
  551. condition = &api.NodeCondition{
  552. Type: api.NodeMemoryPressure,
  553. Status: api.ConditionUnknown,
  554. }
  555. // cannot be appended to node.Status.Conditions here because it gets
  556. // copied to the slice. So if we append to the slice here none of the
  557. // updates we make below are reflected in the slice.
  558. newCondition = true
  559. }
  560. // Update the heartbeat time
  561. condition.LastHeartbeatTime = currentTime
  562. // Note: The conditions below take care of the case when a new NodeMemoryPressure condition is
  563. // created and as well as the case when the condition already exists. When a new condition
  564. // is created its status is set to api.ConditionUnknown which matches either
  565. // condition.Status != api.ConditionTrue or
  566. // condition.Status != api.ConditionFalse in the conditions below depending on whether
  567. // the kubelet is under memory pressure or not.
  568. if kl.evictionManager.IsUnderMemoryPressure() {
  569. if condition.Status != api.ConditionTrue {
  570. condition.Status = api.ConditionTrue
  571. condition.Reason = "KubeletHasInsufficientMemory"
  572. condition.Message = "kubelet has insufficient memory available"
  573. condition.LastTransitionTime = currentTime
  574. kl.recordNodeStatusEvent(api.EventTypeNormal, "NodeHasInsufficientMemory")
  575. }
  576. } else {
  577. if condition.Status != api.ConditionFalse {
  578. condition.Status = api.ConditionFalse
  579. condition.Reason = "KubeletHasSufficientMemory"
  580. condition.Message = "kubelet has sufficient memory available"
  581. condition.LastTransitionTime = currentTime
  582. kl.recordNodeStatusEvent(api.EventTypeNormal, "NodeHasSufficientMemory")
  583. }
  584. }
  585. if newCondition {
  586. node.Status.Conditions = append(node.Status.Conditions, *condition)
  587. }
  588. }
  589. // setNodeDiskPressureCondition for the node.
  590. // TODO: this needs to move somewhere centralized...
  591. func (kl *Kubelet) setNodeDiskPressureCondition(node *api.Node) {
  592. currentTime := unversioned.NewTime(kl.clock.Now())
  593. var condition *api.NodeCondition
  594. // Check if NodeDiskPressure condition already exists and if it does, just pick it up for update.
  595. for i := range node.Status.Conditions {
  596. if node.Status.Conditions[i].Type == api.NodeDiskPressure {
  597. condition = &node.Status.Conditions[i]
  598. }
  599. }
  600. newCondition := false
  601. // If the NodeDiskPressure condition doesn't exist, create one
  602. if condition == nil {
  603. condition = &api.NodeCondition{
  604. Type: api.NodeDiskPressure,
  605. Status: api.ConditionUnknown,
  606. }
  607. // cannot be appended to node.Status.Conditions here because it gets
  608. // copied to the slice. So if we append to the slice here none of the
  609. // updates we make below are reflected in the slice.
  610. newCondition = true
  611. }
  612. // Update the heartbeat time
  613. condition.LastHeartbeatTime = currentTime
  614. // Note: The conditions below take care of the case when a new NodeDiskressure condition is
  615. // created and as well as the case when the condition already exists. When a new condition
  616. // is created its status is set to api.ConditionUnknown which matches either
  617. // condition.Status != api.ConditionTrue or
  618. // condition.Status != api.ConditionFalse in the conditions below depending on whether
  619. // the kubelet is under disk pressure or not.
  620. if kl.evictionManager.IsUnderDiskPressure() {
  621. if condition.Status != api.ConditionTrue {
  622. condition.Status = api.ConditionTrue
  623. condition.Reason = "KubeletHasDiskPressure"
  624. condition.Message = "kubelet has disk pressure"
  625. condition.LastTransitionTime = currentTime
  626. kl.recordNodeStatusEvent(api.EventTypeNormal, "NodeHasDiskPressure")
  627. }
  628. } else {
  629. if condition.Status != api.ConditionFalse {
  630. condition.Status = api.ConditionFalse
  631. condition.Reason = "KubeletHasNoDiskPressure"
  632. condition.Message = "kubelet has no disk pressure"
  633. condition.LastTransitionTime = currentTime
  634. kl.recordNodeStatusEvent(api.EventTypeNormal, "NodeHasNoDiskPressure")
  635. }
  636. }
  637. if newCondition {
  638. node.Status.Conditions = append(node.Status.Conditions, *condition)
  639. }
  640. }
  641. // Set OODcondition for the node.
  642. func (kl *Kubelet) setNodeOODCondition(node *api.Node) {
  643. currentTime := unversioned.NewTime(kl.clock.Now())
  644. var nodeOODCondition *api.NodeCondition
  645. // Check if NodeOutOfDisk condition already exists and if it does, just pick it up for update.
  646. for i := range node.Status.Conditions {
  647. if node.Status.Conditions[i].Type == api.NodeOutOfDisk {
  648. nodeOODCondition = &node.Status.Conditions[i]
  649. }
  650. }
  651. newOODCondition := false
  652. // If the NodeOutOfDisk condition doesn't exist, create one.
  653. if nodeOODCondition == nil {
  654. nodeOODCondition = &api.NodeCondition{
  655. Type: api.NodeOutOfDisk,
  656. Status: api.ConditionUnknown,
  657. }
  658. // nodeOODCondition cannot be appended to node.Status.Conditions here because it gets
  659. // copied to the slice. So if we append nodeOODCondition to the slice here none of the
  660. // updates we make to nodeOODCondition below are reflected in the slice.
  661. newOODCondition = true
  662. }
  663. // Update the heartbeat time irrespective of all the conditions.
  664. nodeOODCondition.LastHeartbeatTime = currentTime
  665. // Note: The conditions below take care of the case when a new NodeOutOfDisk condition is
  666. // created and as well as the case when the condition already exists. When a new condition
  667. // is created its status is set to api.ConditionUnknown which matches either
  668. // nodeOODCondition.Status != api.ConditionTrue or
  669. // nodeOODCondition.Status != api.ConditionFalse in the conditions below depending on whether
  670. // the kubelet is out of disk or not.
  671. if kl.isOutOfDisk() {
  672. if nodeOODCondition.Status != api.ConditionTrue {
  673. nodeOODCondition.Status = api.ConditionTrue
  674. nodeOODCondition.Reason = "KubeletOutOfDisk"
  675. nodeOODCondition.Message = "out of disk space"
  676. nodeOODCondition.LastTransitionTime = currentTime
  677. kl.recordNodeStatusEvent(api.EventTypeNormal, "NodeOutOfDisk")
  678. }
  679. } else {
  680. if nodeOODCondition.Status != api.ConditionFalse {
  681. // Update the out of disk condition when the condition status is unknown even if we
  682. // are within the outOfDiskTransitionFrequency duration. We do this to set the
  683. // condition status correctly at kubelet startup.
  684. if nodeOODCondition.Status == api.ConditionUnknown || kl.clock.Since(nodeOODCondition.LastTransitionTime.Time) >= kl.outOfDiskTransitionFrequency {
  685. nodeOODCondition.Status = api.ConditionFalse
  686. nodeOODCondition.Reason = "KubeletHasSufficientDisk"
  687. nodeOODCondition.Message = "kubelet has sufficient disk space available"
  688. nodeOODCondition.LastTransitionTime = currentTime
  689. kl.recordNodeStatusEvent(api.EventTypeNormal, "NodeHasSufficientDisk")
  690. } else {
  691. glog.Infof("Node condition status for OutOfDisk is false, but last transition time is less than %s", kl.outOfDiskTransitionFrequency)
  692. }
  693. }
  694. }
  695. if newOODCondition {
  696. node.Status.Conditions = append(node.Status.Conditions, *nodeOODCondition)
  697. }
  698. }
  699. // Maintains Node.Spec.Unschedulable value from previous run of tryUpdateNodeStatus()
  700. // TODO: why is this a package var?
  701. var oldNodeUnschedulable bool
  702. // record if node schedulable change.
  703. func (kl *Kubelet) recordNodeSchedulableEvent(node *api.Node) {
  704. if oldNodeUnschedulable != node.Spec.Unschedulable {
  705. if node.Spec.Unschedulable {
  706. kl.recordNodeStatusEvent(api.EventTypeNormal, events.NodeNotSchedulable)
  707. } else {
  708. kl.recordNodeStatusEvent(api.EventTypeNormal, events.NodeSchedulable)
  709. }
  710. oldNodeUnschedulable = node.Spec.Unschedulable
  711. }
  712. }
  713. // Update VolumesInUse field in Node Status
  714. func (kl *Kubelet) setNodeVolumesInUseStatus(node *api.Node) {
  715. node.Status.VolumesInUse = kl.volumeManager.GetVolumesInUse()
  716. }
  717. // setNodeStatus fills in the Status fields of the given Node, overwriting
  718. // any fields that are currently set.
  719. // TODO(madhusudancs): Simplify the logic for setting node conditions and
  720. // refactor the node status condition code out to a different file.
  721. func (kl *Kubelet) setNodeStatus(node *api.Node) error {
  722. for _, f := range kl.setNodeStatusFuncs {
  723. if err := f(node); err != nil {
  724. return err
  725. }
  726. }
  727. return nil
  728. }
  729. // defaultNodeStatusFuncs is a factory that generates the default set of
  730. // setNodeStatus funcs
  731. func (kl *Kubelet) defaultNodeStatusFuncs() []func(*api.Node) error {
  732. // initial set of node status update handlers, can be modified by Option's
  733. withoutError := func(f func(*api.Node)) func(*api.Node) error {
  734. return func(n *api.Node) error {
  735. f(n)
  736. return nil
  737. }
  738. }
  739. return []func(*api.Node) error{
  740. kl.setNodeAddress,
  741. withoutError(kl.setNodeStatusInfo),
  742. withoutError(kl.setNodeOODCondition),
  743. withoutError(kl.setNodeMemoryPressureCondition),
  744. withoutError(kl.setNodeDiskPressureCondition),
  745. withoutError(kl.setNodeReadyCondition),
  746. withoutError(kl.setNodeVolumesInUseStatus),
  747. withoutError(kl.recordNodeSchedulableEvent),
  748. }
  749. }
  750. // SetNodeStatus returns a functional Option that adds the given node status
  751. // update handler to the Kubelet
  752. func SetNodeStatus(f func(*api.Node) error) Option {
  753. return func(k *Kubelet) {
  754. k.setNodeStatusFuncs = append(k.setNodeStatusFuncs, f)
  755. }
  756. }