setters.go 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786
  1. /*
  2. Copyright 2018 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 nodestatus
  14. import (
  15. "fmt"
  16. "math"
  17. "net"
  18. goruntime "runtime"
  19. "strings"
  20. "time"
  21. cadvisorapiv1 "github.com/google/cadvisor/info/v1"
  22. "k8s.io/api/core/v1"
  23. "k8s.io/apimachinery/pkg/api/resource"
  24. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  25. "k8s.io/apimachinery/pkg/util/errors"
  26. utilnet "k8s.io/apimachinery/pkg/util/net"
  27. utilfeature "k8s.io/apiserver/pkg/util/feature"
  28. cloudprovider "k8s.io/cloud-provider"
  29. "k8s.io/component-base/version"
  30. v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
  31. "k8s.io/kubernetes/pkg/features"
  32. kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
  33. "k8s.io/kubernetes/pkg/kubelet/cadvisor"
  34. "k8s.io/kubernetes/pkg/kubelet/cm"
  35. kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
  36. "k8s.io/kubernetes/pkg/kubelet/events"
  37. "k8s.io/kubernetes/pkg/volume"
  38. "k8s.io/klog"
  39. )
  40. const (
  41. // MaxNamesPerImageInNodeStatus is max number of names
  42. // per image stored in the node status.
  43. MaxNamesPerImageInNodeStatus = 5
  44. )
  45. // Setter modifies the node in-place, and returns an error if the modification failed.
  46. // Setters may partially mutate the node before returning an error.
  47. type Setter func(node *v1.Node) error
  48. // NodeAddress returns a Setter that updates address-related information on the node.
  49. func NodeAddress(nodeIP net.IP, // typically Kubelet.nodeIP
  50. validateNodeIPFunc func(net.IP) error, // typically Kubelet.nodeIPValidator
  51. hostname string, // typically Kubelet.hostname
  52. hostnameOverridden bool, // was the hostname force set?
  53. externalCloudProvider bool, // typically Kubelet.externalCloudProvider
  54. cloud cloudprovider.Interface, // typically Kubelet.cloud
  55. nodeAddressesFunc func() ([]v1.NodeAddress, error), // typically Kubelet.cloudResourceSyncManager.NodeAddresses
  56. ) Setter {
  57. preferIPv4 := nodeIP == nil || nodeIP.To4() != nil
  58. isPreferredIPFamily := func(ip net.IP) bool { return (ip.To4() != nil) == preferIPv4 }
  59. nodeIPSpecified := nodeIP != nil && !nodeIP.IsUnspecified()
  60. return func(node *v1.Node) error {
  61. if nodeIPSpecified {
  62. if err := validateNodeIPFunc(nodeIP); err != nil {
  63. return fmt.Errorf("failed to validate nodeIP: %v", err)
  64. }
  65. klog.V(2).Infof("Using node IP: %q", nodeIP.String())
  66. }
  67. if externalCloudProvider {
  68. if nodeIPSpecified {
  69. if node.ObjectMeta.Annotations == nil {
  70. node.ObjectMeta.Annotations = make(map[string]string)
  71. }
  72. node.ObjectMeta.Annotations[kubeletapis.AnnotationProvidedIPAddr] = nodeIP.String()
  73. }
  74. // If --cloud-provider=external and node address is already set,
  75. // then we return early because provider set addresses should take precedence.
  76. // Otherwise, we try to look up the node IP and let the cloud provider override it later
  77. // This should alleviate a lot of the bootstrapping issues with out-of-tree providers
  78. if len(node.Status.Addresses) > 0 {
  79. return nil
  80. }
  81. }
  82. if cloud != nil {
  83. cloudNodeAddresses, err := nodeAddressesFunc()
  84. if err != nil {
  85. return err
  86. }
  87. var nodeAddresses []v1.NodeAddress
  88. // For every address supplied by the cloud provider that matches nodeIP, nodeIP is the enforced node address for
  89. // that address Type (like InternalIP and ExternalIP), meaning other addresses of the same Type are discarded.
  90. // See #61921 for more information: some cloud providers may supply secondary IPs, so nodeIP serves as a way to
  91. // ensure that the correct IPs show up on a Node object.
  92. if nodeIPSpecified {
  93. enforcedNodeAddresses := []v1.NodeAddress{}
  94. nodeIPTypes := make(map[v1.NodeAddressType]bool)
  95. for _, nodeAddress := range cloudNodeAddresses {
  96. if nodeAddress.Address == nodeIP.String() {
  97. enforcedNodeAddresses = append(enforcedNodeAddresses, v1.NodeAddress{Type: nodeAddress.Type, Address: nodeAddress.Address})
  98. nodeIPTypes[nodeAddress.Type] = true
  99. }
  100. }
  101. // nodeIP must be among the addresses supplied by the cloud provider
  102. if len(enforcedNodeAddresses) == 0 {
  103. return fmt.Errorf("failed to get node address from cloud provider that matches ip: %v", nodeIP)
  104. }
  105. // nodeIP was found, now use all other addresses supplied by the cloud provider NOT of the same Type as nodeIP.
  106. for _, nodeAddress := range cloudNodeAddresses {
  107. if !nodeIPTypes[nodeAddress.Type] {
  108. enforcedNodeAddresses = append(enforcedNodeAddresses, v1.NodeAddress{Type: nodeAddress.Type, Address: nodeAddress.Address})
  109. }
  110. }
  111. nodeAddresses = enforcedNodeAddresses
  112. } else if nodeIP != nil {
  113. // nodeIP is "0.0.0.0" or "::"; sort cloudNodeAddresses to
  114. // prefer addresses of the matching family
  115. sortedAddresses := make([]v1.NodeAddress, 0, len(cloudNodeAddresses))
  116. for _, nodeAddress := range cloudNodeAddresses {
  117. ip := net.ParseIP(nodeAddress.Address)
  118. if ip == nil || isPreferredIPFamily(ip) {
  119. sortedAddresses = append(sortedAddresses, nodeAddress)
  120. }
  121. }
  122. for _, nodeAddress := range cloudNodeAddresses {
  123. ip := net.ParseIP(nodeAddress.Address)
  124. if ip != nil && !isPreferredIPFamily(ip) {
  125. sortedAddresses = append(sortedAddresses, nodeAddress)
  126. }
  127. }
  128. nodeAddresses = sortedAddresses
  129. } else {
  130. // If nodeIP is unset, just use the addresses provided by the cloud provider as-is
  131. nodeAddresses = cloudNodeAddresses
  132. }
  133. switch {
  134. case len(cloudNodeAddresses) == 0:
  135. // the cloud provider didn't specify any addresses
  136. nodeAddresses = append(nodeAddresses, v1.NodeAddress{Type: v1.NodeHostName, Address: hostname})
  137. case !hasAddressType(cloudNodeAddresses, v1.NodeHostName) && hasAddressValue(cloudNodeAddresses, hostname):
  138. // the cloud provider didn't specify an address of type Hostname,
  139. // but the auto-detected hostname matched an address reported by the cloud provider,
  140. // so we can add it and count on the value being verifiable via cloud provider metadata
  141. nodeAddresses = append(nodeAddresses, v1.NodeAddress{Type: v1.NodeHostName, Address: hostname})
  142. case hostnameOverridden:
  143. // the hostname was force-set via flag/config.
  144. // this means the hostname might not be able to be validated via cloud provider metadata,
  145. // but was a choice by the kubelet deployer we should honor
  146. var existingHostnameAddress *v1.NodeAddress
  147. for i := range nodeAddresses {
  148. if nodeAddresses[i].Type == v1.NodeHostName {
  149. existingHostnameAddress = &nodeAddresses[i]
  150. break
  151. }
  152. }
  153. if existingHostnameAddress == nil {
  154. // no existing Hostname address found, add it
  155. klog.Warningf("adding overridden hostname of %v to cloudprovider-reported addresses", hostname)
  156. nodeAddresses = append(nodeAddresses, v1.NodeAddress{Type: v1.NodeHostName, Address: hostname})
  157. } else if existingHostnameAddress.Address != hostname {
  158. // override the Hostname address reported by the cloud provider
  159. klog.Warningf("replacing cloudprovider-reported hostname of %v with overridden hostname of %v", existingHostnameAddress.Address, hostname)
  160. existingHostnameAddress.Address = hostname
  161. }
  162. }
  163. node.Status.Addresses = nodeAddresses
  164. } else {
  165. var ipAddr net.IP
  166. var err error
  167. // 1) Use nodeIP if set (and not "0.0.0.0"/"::")
  168. // 2) If the user has specified an IP to HostnameOverride, use it
  169. // 3) Lookup the IP from node name by DNS
  170. // 4) Try to get the IP from the network interface used as default gateway
  171. //
  172. // For steps 3 and 4, IPv4 addresses are preferred to IPv6 addresses
  173. // unless nodeIP is "::", in which case it is reversed.
  174. if nodeIPSpecified {
  175. ipAddr = nodeIP
  176. } else if addr := net.ParseIP(hostname); addr != nil {
  177. ipAddr = addr
  178. } else {
  179. var addrs []net.IP
  180. addrs, _ = net.LookupIP(node.Name)
  181. for _, addr := range addrs {
  182. if err = validateNodeIPFunc(addr); err == nil {
  183. if isPreferredIPFamily(addr) {
  184. ipAddr = addr
  185. break
  186. } else if ipAddr == nil {
  187. ipAddr = addr
  188. }
  189. }
  190. }
  191. if ipAddr == nil {
  192. ipAddr, err = utilnet.ResolveBindAddress(nodeIP)
  193. }
  194. }
  195. if ipAddr == nil {
  196. // We tried everything we could, but the IP address wasn't fetchable; error out
  197. return fmt.Errorf("can't get ip address of node %s. error: %v", node.Name, err)
  198. }
  199. node.Status.Addresses = []v1.NodeAddress{
  200. {Type: v1.NodeInternalIP, Address: ipAddr.String()},
  201. {Type: v1.NodeHostName, Address: hostname},
  202. }
  203. }
  204. return nil
  205. }
  206. }
  207. func hasAddressType(addresses []v1.NodeAddress, addressType v1.NodeAddressType) bool {
  208. for _, address := range addresses {
  209. if address.Type == addressType {
  210. return true
  211. }
  212. }
  213. return false
  214. }
  215. func hasAddressValue(addresses []v1.NodeAddress, addressValue string) bool {
  216. for _, address := range addresses {
  217. if address.Address == addressValue {
  218. return true
  219. }
  220. }
  221. return false
  222. }
  223. // MachineInfo returns a Setter that updates machine-related information on the node.
  224. func MachineInfo(nodeName string,
  225. maxPods int,
  226. podsPerCore int,
  227. machineInfoFunc func() (*cadvisorapiv1.MachineInfo, error), // typically Kubelet.GetCachedMachineInfo
  228. capacityFunc func() v1.ResourceList, // typically Kubelet.containerManager.GetCapacity
  229. devicePluginResourceCapacityFunc func() (v1.ResourceList, v1.ResourceList, []string), // typically Kubelet.containerManager.GetDevicePluginResourceCapacity
  230. nodeAllocatableReservationFunc func() v1.ResourceList, // typically Kubelet.containerManager.GetNodeAllocatableReservation
  231. recordEventFunc func(eventType, event, message string), // typically Kubelet.recordEvent
  232. ) Setter {
  233. return func(node *v1.Node) error {
  234. // Note: avoid blindly overwriting the capacity in case opaque
  235. // resources are being advertised.
  236. if node.Status.Capacity == nil {
  237. node.Status.Capacity = v1.ResourceList{}
  238. }
  239. var devicePluginAllocatable v1.ResourceList
  240. var devicePluginCapacity v1.ResourceList
  241. var removedDevicePlugins []string
  242. // TODO: Post NotReady if we cannot get MachineInfo from cAdvisor. This needs to start
  243. // cAdvisor locally, e.g. for test-cmd.sh, and in integration test.
  244. info, err := machineInfoFunc()
  245. if err != nil {
  246. // TODO(roberthbailey): This is required for test-cmd.sh to pass.
  247. // See if the test should be updated instead.
  248. node.Status.Capacity[v1.ResourceCPU] = *resource.NewMilliQuantity(0, resource.DecimalSI)
  249. node.Status.Capacity[v1.ResourceMemory] = resource.MustParse("0Gi")
  250. node.Status.Capacity[v1.ResourcePods] = *resource.NewQuantity(int64(maxPods), resource.DecimalSI)
  251. klog.Errorf("Error getting machine info: %v", err)
  252. } else {
  253. node.Status.NodeInfo.MachineID = info.MachineID
  254. node.Status.NodeInfo.SystemUUID = info.SystemUUID
  255. for rName, rCap := range cadvisor.CapacityFromMachineInfo(info) {
  256. node.Status.Capacity[rName] = rCap
  257. }
  258. if podsPerCore > 0 {
  259. node.Status.Capacity[v1.ResourcePods] = *resource.NewQuantity(
  260. int64(math.Min(float64(info.NumCores*podsPerCore), float64(maxPods))), resource.DecimalSI)
  261. } else {
  262. node.Status.Capacity[v1.ResourcePods] = *resource.NewQuantity(
  263. int64(maxPods), resource.DecimalSI)
  264. }
  265. if node.Status.NodeInfo.BootID != "" &&
  266. node.Status.NodeInfo.BootID != info.BootID {
  267. // TODO: This requires a transaction, either both node status is updated
  268. // and event is recorded or neither should happen, see issue #6055.
  269. recordEventFunc(v1.EventTypeWarning, events.NodeRebooted,
  270. fmt.Sprintf("Node %s has been rebooted, boot id: %s", nodeName, info.BootID))
  271. }
  272. node.Status.NodeInfo.BootID = info.BootID
  273. if utilfeature.DefaultFeatureGate.Enabled(features.LocalStorageCapacityIsolation) {
  274. // TODO: all the node resources should use ContainerManager.GetCapacity instead of deriving the
  275. // capacity for every node status request
  276. initialCapacity := capacityFunc()
  277. if initialCapacity != nil {
  278. if v, exists := initialCapacity[v1.ResourceEphemeralStorage]; exists {
  279. node.Status.Capacity[v1.ResourceEphemeralStorage] = v
  280. }
  281. }
  282. }
  283. devicePluginCapacity, devicePluginAllocatable, removedDevicePlugins = devicePluginResourceCapacityFunc()
  284. if devicePluginCapacity != nil {
  285. for k, v := range devicePluginCapacity {
  286. if old, ok := node.Status.Capacity[k]; !ok || old.Value() != v.Value() {
  287. klog.V(2).Infof("Update capacity for %s to %d", k, v.Value())
  288. }
  289. node.Status.Capacity[k] = v
  290. }
  291. }
  292. for _, removedResource := range removedDevicePlugins {
  293. klog.V(2).Infof("Set capacity for %s to 0 on device removal", removedResource)
  294. // Set the capacity of the removed resource to 0 instead of
  295. // removing the resource from the node status. This is to indicate
  296. // that the resource is managed by device plugin and had been
  297. // registered before.
  298. //
  299. // This is required to differentiate the device plugin managed
  300. // resources and the cluster-level resources, which are absent in
  301. // node status.
  302. node.Status.Capacity[v1.ResourceName(removedResource)] = *resource.NewQuantity(int64(0), resource.DecimalSI)
  303. }
  304. }
  305. // Set Allocatable.
  306. if node.Status.Allocatable == nil {
  307. node.Status.Allocatable = make(v1.ResourceList)
  308. }
  309. // Remove extended resources from allocatable that are no longer
  310. // present in capacity.
  311. for k := range node.Status.Allocatable {
  312. _, found := node.Status.Capacity[k]
  313. if !found && v1helper.IsExtendedResourceName(k) {
  314. delete(node.Status.Allocatable, k)
  315. }
  316. }
  317. allocatableReservation := nodeAllocatableReservationFunc()
  318. for k, v := range node.Status.Capacity {
  319. value := v.DeepCopy()
  320. if res, exists := allocatableReservation[k]; exists {
  321. value.Sub(res)
  322. }
  323. if value.Sign() < 0 {
  324. // Negative Allocatable resources don't make sense.
  325. value.Set(0)
  326. }
  327. node.Status.Allocatable[k] = value
  328. }
  329. if devicePluginAllocatable != nil {
  330. for k, v := range devicePluginAllocatable {
  331. if old, ok := node.Status.Allocatable[k]; !ok || old.Value() != v.Value() {
  332. klog.V(2).Infof("Update allocatable for %s to %d", k, v.Value())
  333. }
  334. node.Status.Allocatable[k] = v
  335. }
  336. }
  337. // for every huge page reservation, we need to remove it from allocatable memory
  338. for k, v := range node.Status.Capacity {
  339. if v1helper.IsHugePageResourceName(k) {
  340. allocatableMemory := node.Status.Allocatable[v1.ResourceMemory]
  341. value := v.DeepCopy()
  342. allocatableMemory.Sub(value)
  343. if allocatableMemory.Sign() < 0 {
  344. // Negative Allocatable resources don't make sense.
  345. allocatableMemory.Set(0)
  346. }
  347. node.Status.Allocatable[v1.ResourceMemory] = allocatableMemory
  348. }
  349. }
  350. return nil
  351. }
  352. }
  353. // VersionInfo returns a Setter that updates version-related information on the node.
  354. func VersionInfo(versionInfoFunc func() (*cadvisorapiv1.VersionInfo, error), // typically Kubelet.cadvisor.VersionInfo
  355. runtimeTypeFunc func() string, // typically Kubelet.containerRuntime.Type
  356. runtimeVersionFunc func() (kubecontainer.Version, error), // typically Kubelet.containerRuntime.Version
  357. ) Setter {
  358. return func(node *v1.Node) error {
  359. verinfo, err := versionInfoFunc()
  360. if err != nil {
  361. return fmt.Errorf("error getting version info: %v", err)
  362. }
  363. node.Status.NodeInfo.KernelVersion = verinfo.KernelVersion
  364. node.Status.NodeInfo.OSImage = verinfo.ContainerOsVersion
  365. runtimeVersion := "Unknown"
  366. if runtimeVer, err := runtimeVersionFunc(); err == nil {
  367. runtimeVersion = runtimeVer.String()
  368. }
  369. node.Status.NodeInfo.ContainerRuntimeVersion = fmt.Sprintf("%s://%s", runtimeTypeFunc(), runtimeVersion)
  370. node.Status.NodeInfo.KubeletVersion = version.Get().String()
  371. // TODO: kube-proxy might be different version from kubelet in the future
  372. node.Status.NodeInfo.KubeProxyVersion = version.Get().String()
  373. return nil
  374. }
  375. }
  376. // DaemonEndpoints returns a Setter that updates the daemon endpoints on the node.
  377. func DaemonEndpoints(daemonEndpoints *v1.NodeDaemonEndpoints) Setter {
  378. return func(node *v1.Node) error {
  379. node.Status.DaemonEndpoints = *daemonEndpoints
  380. return nil
  381. }
  382. }
  383. // Images returns a Setter that updates the images on the node.
  384. // imageListFunc is expected to return a list of images sorted in descending order by image size.
  385. // nodeStatusMaxImages is ignored if set to -1.
  386. func Images(nodeStatusMaxImages int32,
  387. imageListFunc func() ([]kubecontainer.Image, error), // typically Kubelet.imageManager.GetImageList
  388. ) Setter {
  389. return func(node *v1.Node) error {
  390. // Update image list of this node
  391. var imagesOnNode []v1.ContainerImage
  392. containerImages, err := imageListFunc()
  393. if err != nil {
  394. node.Status.Images = imagesOnNode
  395. return fmt.Errorf("error getting image list: %v", err)
  396. }
  397. // we expect imageListFunc to return a sorted list, so we just need to truncate
  398. if int(nodeStatusMaxImages) > -1 &&
  399. int(nodeStatusMaxImages) < len(containerImages) {
  400. containerImages = containerImages[0:nodeStatusMaxImages]
  401. }
  402. for _, image := range containerImages {
  403. names := append(image.RepoDigests, image.RepoTags...)
  404. // Report up to MaxNamesPerImageInNodeStatus names per image.
  405. if len(names) > MaxNamesPerImageInNodeStatus {
  406. names = names[0:MaxNamesPerImageInNodeStatus]
  407. }
  408. imagesOnNode = append(imagesOnNode, v1.ContainerImage{
  409. Names: names,
  410. SizeBytes: image.Size,
  411. })
  412. }
  413. node.Status.Images = imagesOnNode
  414. return nil
  415. }
  416. }
  417. // GoRuntime returns a Setter that sets GOOS and GOARCH on the node.
  418. func GoRuntime() Setter {
  419. return func(node *v1.Node) error {
  420. node.Status.NodeInfo.OperatingSystem = goruntime.GOOS
  421. node.Status.NodeInfo.Architecture = goruntime.GOARCH
  422. return nil
  423. }
  424. }
  425. // ReadyCondition returns a Setter that updates the v1.NodeReady condition on the node.
  426. func ReadyCondition(
  427. nowFunc func() time.Time, // typically Kubelet.clock.Now
  428. runtimeErrorsFunc func() error, // typically Kubelet.runtimeState.runtimeErrors
  429. networkErrorsFunc func() error, // typically Kubelet.runtimeState.networkErrors
  430. storageErrorsFunc func() error, // typically Kubelet.runtimeState.storageErrors
  431. appArmorValidateHostFunc func() error, // typically Kubelet.appArmorValidator.ValidateHost, might be nil depending on whether there was an appArmorValidator
  432. cmStatusFunc func() cm.Status, // typically Kubelet.containerManager.Status
  433. recordEventFunc func(eventType, event string), // typically Kubelet.recordNodeStatusEvent
  434. ) Setter {
  435. return func(node *v1.Node) error {
  436. // NOTE(aaronlevy): NodeReady condition needs to be the last in the list of node conditions.
  437. // This is due to an issue with version skewed kubelet and master components.
  438. // ref: https://github.com/kubernetes/kubernetes/issues/16961
  439. currentTime := metav1.NewTime(nowFunc())
  440. newNodeReadyCondition := v1.NodeCondition{
  441. Type: v1.NodeReady,
  442. Status: v1.ConditionTrue,
  443. Reason: "KubeletReady",
  444. Message: "kubelet is posting ready status",
  445. LastHeartbeatTime: currentTime,
  446. }
  447. errs := []error{runtimeErrorsFunc(), networkErrorsFunc(), storageErrorsFunc()}
  448. requiredCapacities := []v1.ResourceName{v1.ResourceCPU, v1.ResourceMemory, v1.ResourcePods}
  449. if utilfeature.DefaultFeatureGate.Enabled(features.LocalStorageCapacityIsolation) {
  450. requiredCapacities = append(requiredCapacities, v1.ResourceEphemeralStorage)
  451. }
  452. missingCapacities := []string{}
  453. for _, resource := range requiredCapacities {
  454. if _, found := node.Status.Capacity[resource]; !found {
  455. missingCapacities = append(missingCapacities, string(resource))
  456. }
  457. }
  458. if len(missingCapacities) > 0 {
  459. errs = append(errs, fmt.Errorf("missing node capacity for resources: %s", strings.Join(missingCapacities, ", ")))
  460. }
  461. if aggregatedErr := errors.NewAggregate(errs); aggregatedErr != nil {
  462. newNodeReadyCondition = v1.NodeCondition{
  463. Type: v1.NodeReady,
  464. Status: v1.ConditionFalse,
  465. Reason: "KubeletNotReady",
  466. Message: aggregatedErr.Error(),
  467. LastHeartbeatTime: currentTime,
  468. }
  469. }
  470. // Append AppArmor status if it's enabled.
  471. // TODO(tallclair): This is a temporary message until node feature reporting is added.
  472. if appArmorValidateHostFunc != nil && newNodeReadyCondition.Status == v1.ConditionTrue {
  473. if err := appArmorValidateHostFunc(); err == nil {
  474. newNodeReadyCondition.Message = fmt.Sprintf("%s. AppArmor enabled", newNodeReadyCondition.Message)
  475. }
  476. }
  477. // Record any soft requirements that were not met in the container manager.
  478. status := cmStatusFunc()
  479. if status.SoftRequirements != nil {
  480. newNodeReadyCondition.Message = fmt.Sprintf("%s. WARNING: %s", newNodeReadyCondition.Message, status.SoftRequirements.Error())
  481. }
  482. readyConditionUpdated := false
  483. needToRecordEvent := false
  484. for i := range node.Status.Conditions {
  485. if node.Status.Conditions[i].Type == v1.NodeReady {
  486. if node.Status.Conditions[i].Status == newNodeReadyCondition.Status {
  487. newNodeReadyCondition.LastTransitionTime = node.Status.Conditions[i].LastTransitionTime
  488. } else {
  489. newNodeReadyCondition.LastTransitionTime = currentTime
  490. needToRecordEvent = true
  491. }
  492. node.Status.Conditions[i] = newNodeReadyCondition
  493. readyConditionUpdated = true
  494. break
  495. }
  496. }
  497. if !readyConditionUpdated {
  498. newNodeReadyCondition.LastTransitionTime = currentTime
  499. node.Status.Conditions = append(node.Status.Conditions, newNodeReadyCondition)
  500. }
  501. if needToRecordEvent {
  502. if newNodeReadyCondition.Status == v1.ConditionTrue {
  503. recordEventFunc(v1.EventTypeNormal, events.NodeReady)
  504. } else {
  505. recordEventFunc(v1.EventTypeNormal, events.NodeNotReady)
  506. klog.Infof("Node became not ready: %+v", newNodeReadyCondition)
  507. }
  508. }
  509. return nil
  510. }
  511. }
  512. // MemoryPressureCondition returns a Setter that updates the v1.NodeMemoryPressure condition on the node.
  513. func MemoryPressureCondition(nowFunc func() time.Time, // typically Kubelet.clock.Now
  514. pressureFunc func() bool, // typically Kubelet.evictionManager.IsUnderMemoryPressure
  515. recordEventFunc func(eventType, event string), // typically Kubelet.recordNodeStatusEvent
  516. ) Setter {
  517. return func(node *v1.Node) error {
  518. currentTime := metav1.NewTime(nowFunc())
  519. var condition *v1.NodeCondition
  520. // Check if NodeMemoryPressure condition already exists and if it does, just pick it up for update.
  521. for i := range node.Status.Conditions {
  522. if node.Status.Conditions[i].Type == v1.NodeMemoryPressure {
  523. condition = &node.Status.Conditions[i]
  524. }
  525. }
  526. newCondition := false
  527. // If the NodeMemoryPressure condition doesn't exist, create one
  528. if condition == nil {
  529. condition = &v1.NodeCondition{
  530. Type: v1.NodeMemoryPressure,
  531. Status: v1.ConditionUnknown,
  532. }
  533. // cannot be appended to node.Status.Conditions here because it gets
  534. // copied to the slice. So if we append to the slice here none of the
  535. // updates we make below are reflected in the slice.
  536. newCondition = true
  537. }
  538. // Update the heartbeat time
  539. condition.LastHeartbeatTime = currentTime
  540. // Note: The conditions below take care of the case when a new NodeMemoryPressure condition is
  541. // created and as well as the case when the condition already exists. When a new condition
  542. // is created its status is set to v1.ConditionUnknown which matches either
  543. // condition.Status != v1.ConditionTrue or
  544. // condition.Status != v1.ConditionFalse in the conditions below depending on whether
  545. // the kubelet is under memory pressure or not.
  546. if pressureFunc() {
  547. if condition.Status != v1.ConditionTrue {
  548. condition.Status = v1.ConditionTrue
  549. condition.Reason = "KubeletHasInsufficientMemory"
  550. condition.Message = "kubelet has insufficient memory available"
  551. condition.LastTransitionTime = currentTime
  552. recordEventFunc(v1.EventTypeNormal, "NodeHasInsufficientMemory")
  553. }
  554. } else if condition.Status != v1.ConditionFalse {
  555. condition.Status = v1.ConditionFalse
  556. condition.Reason = "KubeletHasSufficientMemory"
  557. condition.Message = "kubelet has sufficient memory available"
  558. condition.LastTransitionTime = currentTime
  559. recordEventFunc(v1.EventTypeNormal, "NodeHasSufficientMemory")
  560. }
  561. if newCondition {
  562. node.Status.Conditions = append(node.Status.Conditions, *condition)
  563. }
  564. return nil
  565. }
  566. }
  567. // PIDPressureCondition returns a Setter that updates the v1.NodePIDPressure condition on the node.
  568. func PIDPressureCondition(nowFunc func() time.Time, // typically Kubelet.clock.Now
  569. pressureFunc func() bool, // typically Kubelet.evictionManager.IsUnderPIDPressure
  570. recordEventFunc func(eventType, event string), // typically Kubelet.recordNodeStatusEvent
  571. ) Setter {
  572. return func(node *v1.Node) error {
  573. currentTime := metav1.NewTime(nowFunc())
  574. var condition *v1.NodeCondition
  575. // Check if NodePIDPressure condition already exists and if it does, just pick it up for update.
  576. for i := range node.Status.Conditions {
  577. if node.Status.Conditions[i].Type == v1.NodePIDPressure {
  578. condition = &node.Status.Conditions[i]
  579. }
  580. }
  581. newCondition := false
  582. // If the NodePIDPressure condition doesn't exist, create one
  583. if condition == nil {
  584. condition = &v1.NodeCondition{
  585. Type: v1.NodePIDPressure,
  586. Status: v1.ConditionUnknown,
  587. }
  588. // cannot be appended to node.Status.Conditions here because it gets
  589. // copied to the slice. So if we append to the slice here none of the
  590. // updates we make below are reflected in the slice.
  591. newCondition = true
  592. }
  593. // Update the heartbeat time
  594. condition.LastHeartbeatTime = currentTime
  595. // Note: The conditions below take care of the case when a new NodePIDPressure condition is
  596. // created and as well as the case when the condition already exists. When a new condition
  597. // is created its status is set to v1.ConditionUnknown which matches either
  598. // condition.Status != v1.ConditionTrue or
  599. // condition.Status != v1.ConditionFalse in the conditions below depending on whether
  600. // the kubelet is under PID pressure or not.
  601. if pressureFunc() {
  602. if condition.Status != v1.ConditionTrue {
  603. condition.Status = v1.ConditionTrue
  604. condition.Reason = "KubeletHasInsufficientPID"
  605. condition.Message = "kubelet has insufficient PID available"
  606. condition.LastTransitionTime = currentTime
  607. recordEventFunc(v1.EventTypeNormal, "NodeHasInsufficientPID")
  608. }
  609. } else if condition.Status != v1.ConditionFalse {
  610. condition.Status = v1.ConditionFalse
  611. condition.Reason = "KubeletHasSufficientPID"
  612. condition.Message = "kubelet has sufficient PID available"
  613. condition.LastTransitionTime = currentTime
  614. recordEventFunc(v1.EventTypeNormal, "NodeHasSufficientPID")
  615. }
  616. if newCondition {
  617. node.Status.Conditions = append(node.Status.Conditions, *condition)
  618. }
  619. return nil
  620. }
  621. }
  622. // DiskPressureCondition returns a Setter that updates the v1.NodeDiskPressure condition on the node.
  623. func DiskPressureCondition(nowFunc func() time.Time, // typically Kubelet.clock.Now
  624. pressureFunc func() bool, // typically Kubelet.evictionManager.IsUnderDiskPressure
  625. recordEventFunc func(eventType, event string), // typically Kubelet.recordNodeStatusEvent
  626. ) Setter {
  627. return func(node *v1.Node) error {
  628. currentTime := metav1.NewTime(nowFunc())
  629. var condition *v1.NodeCondition
  630. // Check if NodeDiskPressure condition already exists and if it does, just pick it up for update.
  631. for i := range node.Status.Conditions {
  632. if node.Status.Conditions[i].Type == v1.NodeDiskPressure {
  633. condition = &node.Status.Conditions[i]
  634. }
  635. }
  636. newCondition := false
  637. // If the NodeDiskPressure condition doesn't exist, create one
  638. if condition == nil {
  639. condition = &v1.NodeCondition{
  640. Type: v1.NodeDiskPressure,
  641. Status: v1.ConditionUnknown,
  642. }
  643. // cannot be appended to node.Status.Conditions here because it gets
  644. // copied to the slice. So if we append to the slice here none of the
  645. // updates we make below are reflected in the slice.
  646. newCondition = true
  647. }
  648. // Update the heartbeat time
  649. condition.LastHeartbeatTime = currentTime
  650. // Note: The conditions below take care of the case when a new NodeDiskPressure condition is
  651. // created and as well as the case when the condition already exists. When a new condition
  652. // is created its status is set to v1.ConditionUnknown which matches either
  653. // condition.Status != v1.ConditionTrue or
  654. // condition.Status != v1.ConditionFalse in the conditions below depending on whether
  655. // the kubelet is under disk pressure or not.
  656. if pressureFunc() {
  657. if condition.Status != v1.ConditionTrue {
  658. condition.Status = v1.ConditionTrue
  659. condition.Reason = "KubeletHasDiskPressure"
  660. condition.Message = "kubelet has disk pressure"
  661. condition.LastTransitionTime = currentTime
  662. recordEventFunc(v1.EventTypeNormal, "NodeHasDiskPressure")
  663. }
  664. } else if condition.Status != v1.ConditionFalse {
  665. condition.Status = v1.ConditionFalse
  666. condition.Reason = "KubeletHasNoDiskPressure"
  667. condition.Message = "kubelet has no disk pressure"
  668. condition.LastTransitionTime = currentTime
  669. recordEventFunc(v1.EventTypeNormal, "NodeHasNoDiskPressure")
  670. }
  671. if newCondition {
  672. node.Status.Conditions = append(node.Status.Conditions, *condition)
  673. }
  674. return nil
  675. }
  676. }
  677. // VolumesInUse returns a Setter that updates the volumes in use on the node.
  678. func VolumesInUse(syncedFunc func() bool, // typically Kubelet.volumeManager.ReconcilerStatesHasBeenSynced
  679. volumesInUseFunc func() []v1.UniqueVolumeName, // typically Kubelet.volumeManager.GetVolumesInUse
  680. ) Setter {
  681. return func(node *v1.Node) error {
  682. // Make sure to only update node status after reconciler starts syncing up states
  683. if syncedFunc() {
  684. node.Status.VolumesInUse = volumesInUseFunc()
  685. }
  686. return nil
  687. }
  688. }
  689. // VolumeLimits returns a Setter that updates the volume limits on the node.
  690. func VolumeLimits(volumePluginListFunc func() []volume.VolumePluginWithAttachLimits, // typically Kubelet.volumePluginMgr.ListVolumePluginWithLimits
  691. ) Setter {
  692. return func(node *v1.Node) error {
  693. if node.Status.Capacity == nil {
  694. node.Status.Capacity = v1.ResourceList{}
  695. }
  696. if node.Status.Allocatable == nil {
  697. node.Status.Allocatable = v1.ResourceList{}
  698. }
  699. pluginWithLimits := volumePluginListFunc()
  700. for _, volumePlugin := range pluginWithLimits {
  701. attachLimits, err := volumePlugin.GetVolumeLimits()
  702. if err != nil {
  703. klog.V(4).Infof("Error getting volume limit for plugin %s", volumePlugin.GetPluginName())
  704. continue
  705. }
  706. for limitKey, value := range attachLimits {
  707. node.Status.Capacity[v1.ResourceName(limitKey)] = *resource.NewQuantity(value, resource.DecimalSI)
  708. node.Status.Allocatable[v1.ResourceName(limitKey)] = *resource.NewQuantity(value, resource.DecimalSI)
  709. }
  710. }
  711. return nil
  712. }
  713. }