setters_test.go 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830
  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. "errors"
  16. "fmt"
  17. "net"
  18. "sort"
  19. "strconv"
  20. "testing"
  21. "time"
  22. cadvisorapiv1 "github.com/google/cadvisor/info/v1"
  23. "k8s.io/api/core/v1"
  24. apiequality "k8s.io/apimachinery/pkg/api/equality"
  25. "k8s.io/apimachinery/pkg/api/resource"
  26. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  27. "k8s.io/apimachinery/pkg/util/diff"
  28. "k8s.io/apimachinery/pkg/util/rand"
  29. "k8s.io/apimachinery/pkg/util/uuid"
  30. cloudprovider "k8s.io/cloud-provider"
  31. fakecloud "k8s.io/cloud-provider/fake"
  32. "k8s.io/component-base/version"
  33. "k8s.io/kubernetes/pkg/kubelet/cm"
  34. kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
  35. kubecontainertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
  36. "k8s.io/kubernetes/pkg/kubelet/events"
  37. "k8s.io/kubernetes/pkg/kubelet/util/sliceutils"
  38. "k8s.io/kubernetes/pkg/volume"
  39. volumetest "k8s.io/kubernetes/pkg/volume/testing"
  40. "github.com/stretchr/testify/assert"
  41. "github.com/stretchr/testify/require"
  42. )
  43. const (
  44. testKubeletHostname = "127.0.0.1"
  45. )
  46. // TODO(mtaufen): below is ported from the old kubelet_node_status_test.go code, potentially add more test coverage for NodeAddress setter in future
  47. func TestNodeAddress(t *testing.T) {
  48. cases := []struct {
  49. name string
  50. hostnameOverride bool
  51. nodeIP net.IP
  52. externalCloudProvider bool
  53. nodeAddresses []v1.NodeAddress
  54. expectedAddresses []v1.NodeAddress
  55. shouldError bool
  56. }{
  57. {
  58. name: "A single InternalIP",
  59. nodeIP: net.ParseIP("10.1.1.1"),
  60. nodeAddresses: []v1.NodeAddress{
  61. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  62. {Type: v1.NodeHostName, Address: testKubeletHostname},
  63. },
  64. expectedAddresses: []v1.NodeAddress{
  65. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  66. {Type: v1.NodeHostName, Address: testKubeletHostname},
  67. },
  68. shouldError: false,
  69. },
  70. {
  71. name: "NodeIP is external",
  72. nodeIP: net.ParseIP("55.55.55.55"),
  73. nodeAddresses: []v1.NodeAddress{
  74. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  75. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  76. {Type: v1.NodeHostName, Address: testKubeletHostname},
  77. },
  78. expectedAddresses: []v1.NodeAddress{
  79. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  80. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  81. {Type: v1.NodeHostName, Address: testKubeletHostname},
  82. },
  83. shouldError: false,
  84. },
  85. {
  86. // Accommodating #45201 and #49202
  87. name: "InternalIP and ExternalIP are the same",
  88. nodeIP: net.ParseIP("55.55.55.55"),
  89. nodeAddresses: []v1.NodeAddress{
  90. {Type: v1.NodeInternalIP, Address: "44.44.44.44"},
  91. {Type: v1.NodeExternalIP, Address: "44.44.44.44"},
  92. {Type: v1.NodeInternalIP, Address: "55.55.55.55"},
  93. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  94. {Type: v1.NodeHostName, Address: testKubeletHostname},
  95. },
  96. expectedAddresses: []v1.NodeAddress{
  97. {Type: v1.NodeInternalIP, Address: "55.55.55.55"},
  98. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  99. {Type: v1.NodeHostName, Address: testKubeletHostname},
  100. },
  101. shouldError: false,
  102. },
  103. {
  104. name: "An Internal/ExternalIP, an Internal/ExternalDNS",
  105. nodeIP: net.ParseIP("10.1.1.1"),
  106. nodeAddresses: []v1.NodeAddress{
  107. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  108. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  109. {Type: v1.NodeInternalDNS, Address: "ip-10-1-1-1.us-west-2.compute.internal"},
  110. {Type: v1.NodeExternalDNS, Address: "ec2-55-55-55-55.us-west-2.compute.amazonaws.com"},
  111. {Type: v1.NodeHostName, Address: testKubeletHostname},
  112. },
  113. expectedAddresses: []v1.NodeAddress{
  114. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  115. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  116. {Type: v1.NodeInternalDNS, Address: "ip-10-1-1-1.us-west-2.compute.internal"},
  117. {Type: v1.NodeExternalDNS, Address: "ec2-55-55-55-55.us-west-2.compute.amazonaws.com"},
  118. {Type: v1.NodeHostName, Address: testKubeletHostname},
  119. },
  120. shouldError: false,
  121. },
  122. {
  123. name: "An Internal with multiple internal IPs",
  124. nodeIP: net.ParseIP("10.1.1.1"),
  125. nodeAddresses: []v1.NodeAddress{
  126. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  127. {Type: v1.NodeInternalIP, Address: "10.2.2.2"},
  128. {Type: v1.NodeInternalIP, Address: "10.3.3.3"},
  129. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  130. {Type: v1.NodeHostName, Address: testKubeletHostname},
  131. },
  132. expectedAddresses: []v1.NodeAddress{
  133. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  134. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  135. {Type: v1.NodeHostName, Address: testKubeletHostname},
  136. },
  137. shouldError: false,
  138. },
  139. {
  140. name: "An InternalIP that isn't valid: should error",
  141. nodeIP: net.ParseIP("10.2.2.2"),
  142. nodeAddresses: []v1.NodeAddress{
  143. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  144. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  145. {Type: v1.NodeHostName, Address: testKubeletHostname},
  146. },
  147. expectedAddresses: nil,
  148. shouldError: true,
  149. },
  150. {
  151. name: "no cloud reported hostnames",
  152. nodeAddresses: []v1.NodeAddress{},
  153. expectedAddresses: []v1.NodeAddress{
  154. {Type: v1.NodeHostName, Address: testKubeletHostname}, // detected hostname is auto-added in the absence of cloud-reported hostnames
  155. },
  156. shouldError: false,
  157. },
  158. {
  159. name: "cloud reports hostname, no override",
  160. nodeAddresses: []v1.NodeAddress{
  161. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  162. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  163. {Type: v1.NodeHostName, Address: "cloud-host"},
  164. },
  165. expectedAddresses: []v1.NodeAddress{
  166. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  167. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  168. {Type: v1.NodeHostName, Address: "cloud-host"}, // cloud-reported hostname wins over detected hostname
  169. },
  170. shouldError: false,
  171. },
  172. {
  173. name: "cloud reports hostname, nodeIP is set, no override",
  174. nodeIP: net.ParseIP("10.1.1.1"),
  175. nodeAddresses: []v1.NodeAddress{
  176. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  177. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  178. {Type: v1.NodeHostName, Address: "cloud-host"},
  179. },
  180. expectedAddresses: []v1.NodeAddress{
  181. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  182. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  183. {Type: v1.NodeHostName, Address: "cloud-host"}, // cloud-reported hostname wins over detected hostname
  184. },
  185. shouldError: false,
  186. },
  187. {
  188. name: "cloud reports hostname, overridden",
  189. nodeAddresses: []v1.NodeAddress{
  190. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  191. {Type: v1.NodeHostName, Address: "cloud-host"},
  192. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  193. },
  194. expectedAddresses: []v1.NodeAddress{
  195. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  196. {Type: v1.NodeHostName, Address: testKubeletHostname}, // hostname-override wins over cloud-reported hostname
  197. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  198. },
  199. hostnameOverride: true,
  200. shouldError: false,
  201. },
  202. {
  203. name: "cloud provider is external",
  204. nodeIP: net.ParseIP("10.0.0.1"),
  205. nodeAddresses: []v1.NodeAddress{},
  206. externalCloudProvider: true,
  207. expectedAddresses: []v1.NodeAddress{
  208. {Type: v1.NodeInternalIP, Address: "10.0.0.1"},
  209. {Type: v1.NodeHostName, Address: testKubeletHostname},
  210. },
  211. shouldError: false,
  212. },
  213. {
  214. name: "cloud doesn't report hostname, no override, detected hostname mismatch",
  215. nodeAddresses: []v1.NodeAddress{
  216. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  217. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  218. },
  219. expectedAddresses: []v1.NodeAddress{
  220. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  221. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  222. // detected hostname is not auto-added if it doesn't match any cloud-reported addresses
  223. },
  224. shouldError: false,
  225. },
  226. {
  227. name: "cloud doesn't report hostname, no override, detected hostname match",
  228. nodeAddresses: []v1.NodeAddress{
  229. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  230. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  231. {Type: v1.NodeExternalDNS, Address: testKubeletHostname}, // cloud-reported address value matches detected hostname
  232. },
  233. expectedAddresses: []v1.NodeAddress{
  234. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  235. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  236. {Type: v1.NodeExternalDNS, Address: testKubeletHostname},
  237. {Type: v1.NodeHostName, Address: testKubeletHostname}, // detected hostname gets auto-added
  238. },
  239. shouldError: false,
  240. },
  241. {
  242. name: "cloud doesn't report hostname, nodeIP is set, no override, detected hostname match",
  243. nodeIP: net.ParseIP("10.1.1.1"),
  244. nodeAddresses: []v1.NodeAddress{
  245. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  246. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  247. {Type: v1.NodeExternalDNS, Address: testKubeletHostname}, // cloud-reported address value matches detected hostname
  248. },
  249. expectedAddresses: []v1.NodeAddress{
  250. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  251. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  252. {Type: v1.NodeExternalDNS, Address: testKubeletHostname},
  253. {Type: v1.NodeHostName, Address: testKubeletHostname}, // detected hostname gets auto-added
  254. },
  255. shouldError: false,
  256. },
  257. {
  258. name: "cloud doesn't report hostname, nodeIP is set, no override, detected hostname match with same type as nodeIP",
  259. nodeIP: net.ParseIP("10.1.1.1"),
  260. nodeAddresses: []v1.NodeAddress{
  261. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  262. {Type: v1.NodeInternalIP, Address: testKubeletHostname}, // cloud-reported address value matches detected hostname
  263. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  264. },
  265. expectedAddresses: []v1.NodeAddress{
  266. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  267. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  268. {Type: v1.NodeHostName, Address: testKubeletHostname}, // detected hostname gets auto-added
  269. },
  270. shouldError: false,
  271. },
  272. {
  273. name: "cloud doesn't report hostname, hostname override, hostname mismatch",
  274. nodeAddresses: []v1.NodeAddress{
  275. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  276. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  277. },
  278. expectedAddresses: []v1.NodeAddress{
  279. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  280. {Type: v1.NodeExternalIP, Address: "55.55.55.55"},
  281. {Type: v1.NodeHostName, Address: testKubeletHostname}, // overridden hostname gets auto-added
  282. },
  283. hostnameOverride: true,
  284. shouldError: false,
  285. },
  286. {
  287. name: "Dual-stack cloud, IPv4 first, no nodeIP",
  288. nodeAddresses: []v1.NodeAddress{
  289. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  290. {Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
  291. {Type: v1.NodeHostName, Address: testKubeletHostname},
  292. },
  293. expectedAddresses: []v1.NodeAddress{
  294. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  295. {Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
  296. {Type: v1.NodeHostName, Address: testKubeletHostname},
  297. },
  298. shouldError: false,
  299. },
  300. {
  301. name: "Dual-stack cloud, IPv6 first, no nodeIP",
  302. nodeAddresses: []v1.NodeAddress{
  303. {Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
  304. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  305. {Type: v1.NodeHostName, Address: testKubeletHostname},
  306. },
  307. expectedAddresses: []v1.NodeAddress{
  308. {Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
  309. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  310. {Type: v1.NodeHostName, Address: testKubeletHostname},
  311. },
  312. shouldError: false,
  313. },
  314. {
  315. name: "Dual-stack cloud, IPv4 first, request IPv4",
  316. nodeIP: net.ParseIP("0.0.0.0"),
  317. nodeAddresses: []v1.NodeAddress{
  318. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  319. {Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
  320. {Type: v1.NodeHostName, Address: testKubeletHostname},
  321. },
  322. expectedAddresses: []v1.NodeAddress{
  323. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  324. {Type: v1.NodeHostName, Address: testKubeletHostname},
  325. {Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
  326. },
  327. shouldError: false,
  328. },
  329. {
  330. name: "Dual-stack cloud, IPv6 first, request IPv4",
  331. nodeIP: net.ParseIP("0.0.0.0"),
  332. nodeAddresses: []v1.NodeAddress{
  333. {Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
  334. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  335. {Type: v1.NodeHostName, Address: testKubeletHostname},
  336. },
  337. expectedAddresses: []v1.NodeAddress{
  338. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  339. {Type: v1.NodeHostName, Address: testKubeletHostname},
  340. {Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
  341. },
  342. shouldError: false,
  343. },
  344. {
  345. name: "Dual-stack cloud, IPv4 first, request IPv6",
  346. nodeIP: net.ParseIP("::"),
  347. nodeAddresses: []v1.NodeAddress{
  348. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  349. {Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
  350. {Type: v1.NodeHostName, Address: testKubeletHostname},
  351. },
  352. expectedAddresses: []v1.NodeAddress{
  353. {Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
  354. {Type: v1.NodeHostName, Address: testKubeletHostname},
  355. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  356. },
  357. shouldError: false,
  358. },
  359. {
  360. name: "Dual-stack cloud, IPv6 first, request IPv6",
  361. nodeIP: net.ParseIP("::"),
  362. nodeAddresses: []v1.NodeAddress{
  363. {Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
  364. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  365. {Type: v1.NodeHostName, Address: testKubeletHostname},
  366. },
  367. expectedAddresses: []v1.NodeAddress{
  368. {Type: v1.NodeInternalIP, Address: "fc01:1234::5678"},
  369. {Type: v1.NodeHostName, Address: testKubeletHostname},
  370. {Type: v1.NodeInternalIP, Address: "10.1.1.1"},
  371. },
  372. shouldError: false,
  373. },
  374. }
  375. for _, testCase := range cases {
  376. t.Run(testCase.name, func(t *testing.T) {
  377. // testCase setup
  378. existingNode := &v1.Node{
  379. ObjectMeta: metav1.ObjectMeta{Name: testKubeletHostname, Annotations: make(map[string]string)},
  380. Spec: v1.NodeSpec{},
  381. Status: v1.NodeStatus{
  382. Addresses: []v1.NodeAddress{},
  383. },
  384. }
  385. nodeIP := testCase.nodeIP
  386. nodeIPValidator := func(nodeIP net.IP) error {
  387. return nil
  388. }
  389. hostname := testKubeletHostname
  390. nodeAddressesFunc := func() ([]v1.NodeAddress, error) {
  391. return testCase.nodeAddresses, nil
  392. }
  393. // cloud provider is expected to be nil if external provider is set
  394. var cloud cloudprovider.Interface
  395. if testCase.externalCloudProvider {
  396. cloud = nil
  397. } else {
  398. cloud = &fakecloud.Cloud{
  399. Addresses: testCase.nodeAddresses,
  400. Err: nil,
  401. }
  402. }
  403. // construct setter
  404. setter := NodeAddress(nodeIP,
  405. nodeIPValidator,
  406. hostname,
  407. testCase.hostnameOverride,
  408. testCase.externalCloudProvider,
  409. cloud,
  410. nodeAddressesFunc)
  411. // call setter on existing node
  412. err := setter(existingNode)
  413. if err != nil && !testCase.shouldError {
  414. t.Fatalf("unexpected error: %v", err)
  415. } else if err != nil && testCase.shouldError {
  416. // expected an error, and got one, so just return early here
  417. return
  418. }
  419. // Sort both sets for consistent equality
  420. sortNodeAddresses(testCase.expectedAddresses)
  421. sortNodeAddresses(existingNode.Status.Addresses)
  422. assert.True(t, apiequality.Semantic.DeepEqual(testCase.expectedAddresses, existingNode.Status.Addresses),
  423. "Diff: %s", diff.ObjectDiff(testCase.expectedAddresses, existingNode.Status.Addresses))
  424. })
  425. }
  426. }
  427. func TestMachineInfo(t *testing.T) {
  428. const nodeName = "test-node"
  429. type dprc struct {
  430. capacity v1.ResourceList
  431. allocatable v1.ResourceList
  432. inactive []string
  433. }
  434. cases := []struct {
  435. desc string
  436. node *v1.Node
  437. maxPods int
  438. podsPerCore int
  439. machineInfo *cadvisorapiv1.MachineInfo
  440. machineInfoError error
  441. capacity v1.ResourceList
  442. devicePluginResourceCapacity dprc
  443. nodeAllocatableReservation v1.ResourceList
  444. expectNode *v1.Node
  445. expectEvents []testEvent
  446. }{
  447. {
  448. desc: "machine identifiers, basic capacity and allocatable",
  449. node: &v1.Node{},
  450. maxPods: 110,
  451. machineInfo: &cadvisorapiv1.MachineInfo{
  452. MachineID: "MachineID",
  453. SystemUUID: "SystemUUID",
  454. NumCores: 2,
  455. MemoryCapacity: 1024,
  456. },
  457. expectNode: &v1.Node{
  458. Status: v1.NodeStatus{
  459. NodeInfo: v1.NodeSystemInfo{
  460. MachineID: "MachineID",
  461. SystemUUID: "SystemUUID",
  462. },
  463. Capacity: v1.ResourceList{
  464. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  465. v1.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
  466. v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
  467. },
  468. Allocatable: v1.ResourceList{
  469. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  470. v1.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
  471. v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
  472. },
  473. },
  474. },
  475. },
  476. {
  477. desc: "podsPerCore greater than zero, but less than maxPods/cores",
  478. node: &v1.Node{},
  479. maxPods: 10,
  480. podsPerCore: 4,
  481. machineInfo: &cadvisorapiv1.MachineInfo{
  482. NumCores: 2,
  483. MemoryCapacity: 1024,
  484. },
  485. expectNode: &v1.Node{
  486. Status: v1.NodeStatus{
  487. Capacity: v1.ResourceList{
  488. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  489. v1.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
  490. v1.ResourcePods: *resource.NewQuantity(8, resource.DecimalSI),
  491. },
  492. Allocatable: v1.ResourceList{
  493. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  494. v1.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
  495. v1.ResourcePods: *resource.NewQuantity(8, resource.DecimalSI),
  496. },
  497. },
  498. },
  499. },
  500. {
  501. desc: "podsPerCore greater than maxPods/cores",
  502. node: &v1.Node{},
  503. maxPods: 10,
  504. podsPerCore: 6,
  505. machineInfo: &cadvisorapiv1.MachineInfo{
  506. NumCores: 2,
  507. MemoryCapacity: 1024,
  508. },
  509. expectNode: &v1.Node{
  510. Status: v1.NodeStatus{
  511. Capacity: v1.ResourceList{
  512. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  513. v1.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
  514. v1.ResourcePods: *resource.NewQuantity(10, resource.DecimalSI),
  515. },
  516. Allocatable: v1.ResourceList{
  517. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  518. v1.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
  519. v1.ResourcePods: *resource.NewQuantity(10, resource.DecimalSI),
  520. },
  521. },
  522. },
  523. },
  524. {
  525. desc: "allocatable should equal capacity minus reservations",
  526. node: &v1.Node{},
  527. maxPods: 110,
  528. machineInfo: &cadvisorapiv1.MachineInfo{
  529. NumCores: 2,
  530. MemoryCapacity: 1024,
  531. },
  532. nodeAllocatableReservation: v1.ResourceList{
  533. // reserve 1 unit for each resource
  534. v1.ResourceCPU: *resource.NewMilliQuantity(1, resource.DecimalSI),
  535. v1.ResourceMemory: *resource.NewQuantity(1, resource.BinarySI),
  536. v1.ResourcePods: *resource.NewQuantity(1, resource.DecimalSI),
  537. v1.ResourceEphemeralStorage: *resource.NewQuantity(1, resource.BinarySI),
  538. },
  539. expectNode: &v1.Node{
  540. Status: v1.NodeStatus{
  541. Capacity: v1.ResourceList{
  542. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  543. v1.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
  544. v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
  545. },
  546. Allocatable: v1.ResourceList{
  547. v1.ResourceCPU: *resource.NewMilliQuantity(1999, resource.DecimalSI),
  548. v1.ResourceMemory: *resource.NewQuantity(1023, resource.BinarySI),
  549. v1.ResourcePods: *resource.NewQuantity(109, resource.DecimalSI),
  550. },
  551. },
  552. },
  553. },
  554. {
  555. desc: "allocatable memory does not double-count hugepages reservations",
  556. node: &v1.Node{
  557. Status: v1.NodeStatus{
  558. Capacity: v1.ResourceList{
  559. // it's impossible on any real system to reserve 1 byte,
  560. // but we just need to test that the setter does the math
  561. v1.ResourceHugePagesPrefix + "test": *resource.NewQuantity(1, resource.BinarySI),
  562. },
  563. },
  564. },
  565. maxPods: 110,
  566. machineInfo: &cadvisorapiv1.MachineInfo{
  567. NumCores: 2,
  568. MemoryCapacity: 1024,
  569. },
  570. expectNode: &v1.Node{
  571. Status: v1.NodeStatus{
  572. Capacity: v1.ResourceList{
  573. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  574. v1.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
  575. v1.ResourceHugePagesPrefix + "test": *resource.NewQuantity(1, resource.BinarySI),
  576. v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
  577. },
  578. Allocatable: v1.ResourceList{
  579. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  580. // memory has 1-unit difference for hugepages reservation
  581. v1.ResourceMemory: *resource.NewQuantity(1023, resource.BinarySI),
  582. v1.ResourceHugePagesPrefix + "test": *resource.NewQuantity(1, resource.BinarySI),
  583. v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
  584. },
  585. },
  586. },
  587. },
  588. {
  589. desc: "negative capacity resources should be set to 0 in allocatable",
  590. node: &v1.Node{
  591. Status: v1.NodeStatus{
  592. Capacity: v1.ResourceList{
  593. "negative-resource": *resource.NewQuantity(-1, resource.BinarySI),
  594. },
  595. },
  596. },
  597. maxPods: 110,
  598. machineInfo: &cadvisorapiv1.MachineInfo{
  599. NumCores: 2,
  600. MemoryCapacity: 1024,
  601. },
  602. expectNode: &v1.Node{
  603. Status: v1.NodeStatus{
  604. Capacity: v1.ResourceList{
  605. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  606. v1.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
  607. v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
  608. "negative-resource": *resource.NewQuantity(-1, resource.BinarySI),
  609. },
  610. Allocatable: v1.ResourceList{
  611. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  612. v1.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
  613. v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
  614. "negative-resource": *resource.NewQuantity(0, resource.BinarySI),
  615. },
  616. },
  617. },
  618. },
  619. {
  620. desc: "ephemeral storage is reflected in capacity and allocatable",
  621. node: &v1.Node{},
  622. maxPods: 110,
  623. machineInfo: &cadvisorapiv1.MachineInfo{
  624. NumCores: 2,
  625. MemoryCapacity: 1024,
  626. },
  627. capacity: v1.ResourceList{
  628. v1.ResourceEphemeralStorage: *resource.NewQuantity(5000, resource.BinarySI),
  629. },
  630. expectNode: &v1.Node{
  631. Status: v1.NodeStatus{
  632. Capacity: v1.ResourceList{
  633. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  634. v1.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
  635. v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
  636. v1.ResourceEphemeralStorage: *resource.NewQuantity(5000, resource.BinarySI),
  637. },
  638. Allocatable: v1.ResourceList{
  639. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  640. v1.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
  641. v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
  642. v1.ResourceEphemeralStorage: *resource.NewQuantity(5000, resource.BinarySI),
  643. },
  644. },
  645. },
  646. },
  647. {
  648. desc: "device plugin resources are reflected in capacity and allocatable",
  649. node: &v1.Node{},
  650. maxPods: 110,
  651. machineInfo: &cadvisorapiv1.MachineInfo{
  652. NumCores: 2,
  653. MemoryCapacity: 1024,
  654. },
  655. devicePluginResourceCapacity: dprc{
  656. capacity: v1.ResourceList{
  657. "device-plugin": *resource.NewQuantity(1, resource.BinarySI),
  658. },
  659. allocatable: v1.ResourceList{
  660. "device-plugin": *resource.NewQuantity(1, resource.BinarySI),
  661. },
  662. },
  663. expectNode: &v1.Node{
  664. Status: v1.NodeStatus{
  665. Capacity: v1.ResourceList{
  666. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  667. v1.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
  668. v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
  669. "device-plugin": *resource.NewQuantity(1, resource.BinarySI),
  670. },
  671. Allocatable: v1.ResourceList{
  672. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  673. v1.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
  674. v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
  675. "device-plugin": *resource.NewQuantity(1, resource.BinarySI),
  676. },
  677. },
  678. },
  679. },
  680. {
  681. desc: "inactive device plugin resources should have their capacity set to 0",
  682. node: &v1.Node{
  683. Status: v1.NodeStatus{
  684. Capacity: v1.ResourceList{
  685. "inactive": *resource.NewQuantity(1, resource.BinarySI),
  686. },
  687. },
  688. },
  689. maxPods: 110,
  690. machineInfo: &cadvisorapiv1.MachineInfo{
  691. NumCores: 2,
  692. MemoryCapacity: 1024,
  693. },
  694. devicePluginResourceCapacity: dprc{
  695. inactive: []string{"inactive"},
  696. },
  697. expectNode: &v1.Node{
  698. Status: v1.NodeStatus{
  699. Capacity: v1.ResourceList{
  700. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  701. v1.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
  702. v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
  703. "inactive": *resource.NewQuantity(0, resource.BinarySI),
  704. },
  705. Allocatable: v1.ResourceList{
  706. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  707. v1.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
  708. v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
  709. "inactive": *resource.NewQuantity(0, resource.BinarySI),
  710. },
  711. },
  712. },
  713. },
  714. {
  715. desc: "extended resources not present in capacity are removed from allocatable",
  716. node: &v1.Node{
  717. Status: v1.NodeStatus{
  718. Allocatable: v1.ResourceList{
  719. "example.com/extended": *resource.NewQuantity(1, resource.BinarySI),
  720. },
  721. },
  722. },
  723. maxPods: 110,
  724. machineInfo: &cadvisorapiv1.MachineInfo{
  725. NumCores: 2,
  726. MemoryCapacity: 1024,
  727. },
  728. expectNode: &v1.Node{
  729. Status: v1.NodeStatus{
  730. Capacity: v1.ResourceList{
  731. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  732. v1.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
  733. v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
  734. },
  735. Allocatable: v1.ResourceList{
  736. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  737. v1.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
  738. v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
  739. },
  740. },
  741. },
  742. },
  743. {
  744. desc: "on failure to get machine info, allocatable and capacity for memory and cpu are set to 0, pods to maxPods",
  745. node: &v1.Node{},
  746. maxPods: 110,
  747. // podsPerCore is not accounted for when getting machine info fails
  748. podsPerCore: 1,
  749. machineInfoError: fmt.Errorf("foo"),
  750. expectNode: &v1.Node{
  751. Status: v1.NodeStatus{
  752. Capacity: v1.ResourceList{
  753. v1.ResourceCPU: *resource.NewMilliQuantity(0, resource.DecimalSI),
  754. v1.ResourceMemory: resource.MustParse("0Gi"),
  755. v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
  756. },
  757. Allocatable: v1.ResourceList{
  758. v1.ResourceCPU: *resource.NewMilliQuantity(0, resource.DecimalSI),
  759. v1.ResourceMemory: resource.MustParse("0Gi"),
  760. v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
  761. },
  762. },
  763. },
  764. },
  765. {
  766. desc: "node reboot event is recorded",
  767. node: &v1.Node{
  768. Status: v1.NodeStatus{
  769. NodeInfo: v1.NodeSystemInfo{
  770. BootID: "foo",
  771. },
  772. },
  773. },
  774. maxPods: 110,
  775. machineInfo: &cadvisorapiv1.MachineInfo{
  776. BootID: "bar",
  777. NumCores: 2,
  778. MemoryCapacity: 1024,
  779. },
  780. expectNode: &v1.Node{
  781. Status: v1.NodeStatus{
  782. NodeInfo: v1.NodeSystemInfo{
  783. BootID: "bar",
  784. },
  785. Capacity: v1.ResourceList{
  786. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  787. v1.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
  788. v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
  789. },
  790. Allocatable: v1.ResourceList{
  791. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  792. v1.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
  793. v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
  794. },
  795. },
  796. },
  797. expectEvents: []testEvent{
  798. {
  799. eventType: v1.EventTypeWarning,
  800. event: events.NodeRebooted,
  801. message: fmt.Sprintf("Node %s has been rebooted, boot id: %s", nodeName, "bar"),
  802. },
  803. },
  804. },
  805. }
  806. for _, tc := range cases {
  807. t.Run(tc.desc, func(t *testing.T) {
  808. machineInfoFunc := func() (*cadvisorapiv1.MachineInfo, error) {
  809. return tc.machineInfo, tc.machineInfoError
  810. }
  811. capacityFunc := func() v1.ResourceList {
  812. return tc.capacity
  813. }
  814. devicePluginResourceCapacityFunc := func() (v1.ResourceList, v1.ResourceList, []string) {
  815. c := tc.devicePluginResourceCapacity
  816. return c.capacity, c.allocatable, c.inactive
  817. }
  818. nodeAllocatableReservationFunc := func() v1.ResourceList {
  819. return tc.nodeAllocatableReservation
  820. }
  821. events := []testEvent{}
  822. recordEventFunc := func(eventType, event, message string) {
  823. events = append(events, testEvent{
  824. eventType: eventType,
  825. event: event,
  826. message: message,
  827. })
  828. }
  829. // construct setter
  830. setter := MachineInfo(nodeName, tc.maxPods, tc.podsPerCore, machineInfoFunc, capacityFunc,
  831. devicePluginResourceCapacityFunc, nodeAllocatableReservationFunc, recordEventFunc)
  832. // call setter on node
  833. if err := setter(tc.node); err != nil {
  834. t.Fatalf("unexpected error: %v", err)
  835. }
  836. // check expected node
  837. assert.True(t, apiequality.Semantic.DeepEqual(tc.expectNode, tc.node),
  838. "Diff: %s", diff.ObjectDiff(tc.expectNode, tc.node))
  839. // check expected events
  840. require.Equal(t, len(tc.expectEvents), len(events))
  841. for i := range tc.expectEvents {
  842. assert.Equal(t, tc.expectEvents[i], events[i])
  843. }
  844. })
  845. }
  846. }
  847. func TestVersionInfo(t *testing.T) {
  848. cases := []struct {
  849. desc string
  850. node *v1.Node
  851. versionInfo *cadvisorapiv1.VersionInfo
  852. versionInfoError error
  853. runtimeType string
  854. runtimeVersion kubecontainer.Version
  855. runtimeVersionError error
  856. expectNode *v1.Node
  857. expectError error
  858. }{
  859. {
  860. desc: "versions set in node info",
  861. node: &v1.Node{},
  862. versionInfo: &cadvisorapiv1.VersionInfo{
  863. KernelVersion: "KernelVersion",
  864. ContainerOsVersion: "ContainerOSVersion",
  865. },
  866. runtimeType: "RuntimeType",
  867. runtimeVersion: &kubecontainertest.FakeVersion{
  868. Version: "RuntimeVersion",
  869. },
  870. expectNode: &v1.Node{
  871. Status: v1.NodeStatus{
  872. NodeInfo: v1.NodeSystemInfo{
  873. KernelVersion: "KernelVersion",
  874. OSImage: "ContainerOSVersion",
  875. ContainerRuntimeVersion: "RuntimeType://RuntimeVersion",
  876. KubeletVersion: version.Get().String(),
  877. KubeProxyVersion: version.Get().String(),
  878. },
  879. },
  880. },
  881. },
  882. {
  883. desc: "error getting version info",
  884. node: &v1.Node{},
  885. versionInfoError: fmt.Errorf("foo"),
  886. expectNode: &v1.Node{},
  887. expectError: fmt.Errorf("error getting version info: foo"),
  888. },
  889. {
  890. desc: "error getting runtime version results in Unknown runtime",
  891. node: &v1.Node{},
  892. versionInfo: &cadvisorapiv1.VersionInfo{},
  893. runtimeType: "RuntimeType",
  894. runtimeVersionError: fmt.Errorf("foo"),
  895. expectNode: &v1.Node{
  896. Status: v1.NodeStatus{
  897. NodeInfo: v1.NodeSystemInfo{
  898. ContainerRuntimeVersion: "RuntimeType://Unknown",
  899. KubeletVersion: version.Get().String(),
  900. KubeProxyVersion: version.Get().String(),
  901. },
  902. },
  903. },
  904. },
  905. }
  906. for _, tc := range cases {
  907. t.Run(tc.desc, func(t *testing.T) {
  908. versionInfoFunc := func() (*cadvisorapiv1.VersionInfo, error) {
  909. return tc.versionInfo, tc.versionInfoError
  910. }
  911. runtimeTypeFunc := func() string {
  912. return tc.runtimeType
  913. }
  914. runtimeVersionFunc := func() (kubecontainer.Version, error) {
  915. return tc.runtimeVersion, tc.runtimeVersionError
  916. }
  917. // construct setter
  918. setter := VersionInfo(versionInfoFunc, runtimeTypeFunc, runtimeVersionFunc)
  919. // call setter on node
  920. err := setter(tc.node)
  921. require.Equal(t, tc.expectError, err)
  922. // check expected node
  923. assert.True(t, apiequality.Semantic.DeepEqual(tc.expectNode, tc.node),
  924. "Diff: %s", diff.ObjectDiff(tc.expectNode, tc.node))
  925. })
  926. }
  927. }
  928. func TestImages(t *testing.T) {
  929. const (
  930. minImageSize = 23 * 1024 * 1024
  931. maxImageSize = 1000 * 1024 * 1024
  932. )
  933. cases := []struct {
  934. desc string
  935. maxImages int32
  936. imageList []kubecontainer.Image
  937. imageListError error
  938. expectError error
  939. }{
  940. {
  941. desc: "max images enforced",
  942. maxImages: 1,
  943. imageList: makeImageList(2, 1, minImageSize, maxImageSize),
  944. },
  945. {
  946. desc: "no max images cap for -1",
  947. maxImages: -1,
  948. imageList: makeImageList(2, 1, minImageSize, maxImageSize),
  949. },
  950. {
  951. desc: "max names per image enforced",
  952. maxImages: -1,
  953. imageList: makeImageList(1, MaxNamesPerImageInNodeStatus+1, minImageSize, maxImageSize),
  954. },
  955. {
  956. desc: "images are sorted by size, descending",
  957. maxImages: -1,
  958. // makeExpectedImageList will sort them for expectedNode when the test case is run
  959. imageList: []kubecontainer.Image{{Size: 3}, {Size: 1}, {Size: 4}, {Size: 2}},
  960. },
  961. {
  962. desc: "repo digests and tags both show up in image names",
  963. maxImages: -1,
  964. // makeExpectedImageList will use both digests and tags
  965. imageList: []kubecontainer.Image{
  966. {
  967. RepoDigests: []string{"foo", "bar"},
  968. RepoTags: []string{"baz", "quux"},
  969. },
  970. },
  971. },
  972. {
  973. desc: "error getting image list, image list on node is reset to empty",
  974. maxImages: -1,
  975. imageListError: fmt.Errorf("foo"),
  976. expectError: fmt.Errorf("error getting image list: foo"),
  977. },
  978. }
  979. for _, tc := range cases {
  980. t.Run(tc.desc, func(t *testing.T) {
  981. imageListFunc := func() ([]kubecontainer.Image, error) {
  982. // today, imageListFunc is expected to return a sorted list,
  983. // but we may choose to sort in the setter at some future point
  984. // (e.g. if the image cache stopped sorting for us)
  985. sort.Sort(sliceutils.ByImageSize(tc.imageList))
  986. return tc.imageList, tc.imageListError
  987. }
  988. // construct setter
  989. setter := Images(tc.maxImages, imageListFunc)
  990. // call setter on node
  991. node := &v1.Node{}
  992. err := setter(node)
  993. require.Equal(t, tc.expectError, err)
  994. // check expected node, image list should be reset to empty when there is an error
  995. expectNode := &v1.Node{}
  996. if err == nil {
  997. expectNode.Status.Images = makeExpectedImageList(tc.imageList, tc.maxImages, MaxNamesPerImageInNodeStatus)
  998. }
  999. assert.True(t, apiequality.Semantic.DeepEqual(expectNode, node),
  1000. "Diff: %s", diff.ObjectDiff(expectNode, node))
  1001. })
  1002. }
  1003. }
  1004. func TestReadyCondition(t *testing.T) {
  1005. now := time.Now()
  1006. before := now.Add(-time.Second)
  1007. nowFunc := func() time.Time { return now }
  1008. withCapacity := &v1.Node{
  1009. Status: v1.NodeStatus{
  1010. Capacity: v1.ResourceList{
  1011. v1.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
  1012. v1.ResourceMemory: *resource.NewQuantity(10e9, resource.BinarySI),
  1013. v1.ResourcePods: *resource.NewQuantity(100, resource.DecimalSI),
  1014. v1.ResourceEphemeralStorage: *resource.NewQuantity(5000, resource.BinarySI),
  1015. },
  1016. },
  1017. }
  1018. cases := []struct {
  1019. desc string
  1020. node *v1.Node
  1021. runtimeErrors error
  1022. networkErrors error
  1023. storageErrors error
  1024. appArmorValidateHostFunc func() error
  1025. cmStatus cm.Status
  1026. expectConditions []v1.NodeCondition
  1027. expectEvents []testEvent
  1028. }{
  1029. {
  1030. desc: "new, ready",
  1031. node: withCapacity.DeepCopy(),
  1032. expectConditions: []v1.NodeCondition{*makeReadyCondition(true, "kubelet is posting ready status", now, now)},
  1033. // TODO(mtaufen): The current behavior is that we don't send an event for the initial NodeReady condition,
  1034. // the reason for this is unclear, so we may want to actually send an event, and change these test cases
  1035. // to ensure an event is sent.
  1036. },
  1037. {
  1038. desc: "new, ready: apparmor validator passed",
  1039. node: withCapacity.DeepCopy(),
  1040. appArmorValidateHostFunc: func() error { return nil },
  1041. expectConditions: []v1.NodeCondition{*makeReadyCondition(true, "kubelet is posting ready status. AppArmor enabled", now, now)},
  1042. },
  1043. {
  1044. desc: "new, ready: apparmor validator failed",
  1045. node: withCapacity.DeepCopy(),
  1046. appArmorValidateHostFunc: func() error { return fmt.Errorf("foo") },
  1047. // absence of an additional message is understood to mean that AppArmor is disabled
  1048. expectConditions: []v1.NodeCondition{*makeReadyCondition(true, "kubelet is posting ready status", now, now)},
  1049. },
  1050. {
  1051. desc: "new, ready: soft requirement warning",
  1052. node: withCapacity.DeepCopy(),
  1053. cmStatus: cm.Status{
  1054. SoftRequirements: fmt.Errorf("foo"),
  1055. },
  1056. expectConditions: []v1.NodeCondition{*makeReadyCondition(true, "kubelet is posting ready status. WARNING: foo", now, now)},
  1057. },
  1058. {
  1059. desc: "new, not ready: storage errors",
  1060. node: withCapacity.DeepCopy(),
  1061. storageErrors: errors.New("some storage error"),
  1062. expectConditions: []v1.NodeCondition{*makeReadyCondition(false, "some storage error", now, now)},
  1063. },
  1064. {
  1065. desc: "new, not ready: runtime and network errors",
  1066. node: withCapacity.DeepCopy(),
  1067. runtimeErrors: errors.New("runtime"),
  1068. networkErrors: errors.New("network"),
  1069. expectConditions: []v1.NodeCondition{*makeReadyCondition(false, "[runtime, network]", now, now)},
  1070. },
  1071. {
  1072. desc: "new, not ready: missing capacities",
  1073. node: &v1.Node{},
  1074. expectConditions: []v1.NodeCondition{*makeReadyCondition(false, "missing node capacity for resources: cpu, memory, pods, ephemeral-storage", now, now)},
  1075. },
  1076. // the transition tests ensure timestamps are set correctly, no need to test the entire condition matrix in this section
  1077. {
  1078. desc: "transition to ready",
  1079. node: func() *v1.Node {
  1080. node := withCapacity.DeepCopy()
  1081. node.Status.Conditions = []v1.NodeCondition{*makeReadyCondition(false, "", before, before)}
  1082. return node
  1083. }(),
  1084. expectConditions: []v1.NodeCondition{*makeReadyCondition(true, "kubelet is posting ready status", now, now)},
  1085. expectEvents: []testEvent{
  1086. {
  1087. eventType: v1.EventTypeNormal,
  1088. event: events.NodeReady,
  1089. },
  1090. },
  1091. },
  1092. {
  1093. desc: "transition to not ready",
  1094. node: func() *v1.Node {
  1095. node := withCapacity.DeepCopy()
  1096. node.Status.Conditions = []v1.NodeCondition{*makeReadyCondition(true, "", before, before)}
  1097. return node
  1098. }(),
  1099. runtimeErrors: errors.New("foo"),
  1100. expectConditions: []v1.NodeCondition{*makeReadyCondition(false, "foo", now, now)},
  1101. expectEvents: []testEvent{
  1102. {
  1103. eventType: v1.EventTypeNormal,
  1104. event: events.NodeNotReady,
  1105. },
  1106. },
  1107. },
  1108. {
  1109. desc: "ready, no transition",
  1110. node: func() *v1.Node {
  1111. node := withCapacity.DeepCopy()
  1112. node.Status.Conditions = []v1.NodeCondition{*makeReadyCondition(true, "", before, before)}
  1113. return node
  1114. }(),
  1115. expectConditions: []v1.NodeCondition{*makeReadyCondition(true, "kubelet is posting ready status", before, now)},
  1116. expectEvents: []testEvent{},
  1117. },
  1118. {
  1119. desc: "not ready, no transition",
  1120. node: func() *v1.Node {
  1121. node := withCapacity.DeepCopy()
  1122. node.Status.Conditions = []v1.NodeCondition{*makeReadyCondition(false, "", before, before)}
  1123. return node
  1124. }(),
  1125. runtimeErrors: errors.New("foo"),
  1126. expectConditions: []v1.NodeCondition{*makeReadyCondition(false, "foo", before, now)},
  1127. expectEvents: []testEvent{},
  1128. },
  1129. }
  1130. for _, tc := range cases {
  1131. t.Run(tc.desc, func(t *testing.T) {
  1132. runtimeErrorsFunc := func() error {
  1133. return tc.runtimeErrors
  1134. }
  1135. networkErrorsFunc := func() error {
  1136. return tc.networkErrors
  1137. }
  1138. storageErrorsFunc := func() error {
  1139. return tc.storageErrors
  1140. }
  1141. cmStatusFunc := func() cm.Status {
  1142. return tc.cmStatus
  1143. }
  1144. events := []testEvent{}
  1145. recordEventFunc := func(eventType, event string) {
  1146. events = append(events, testEvent{
  1147. eventType: eventType,
  1148. event: event,
  1149. })
  1150. }
  1151. // construct setter
  1152. setter := ReadyCondition(nowFunc, runtimeErrorsFunc, networkErrorsFunc, storageErrorsFunc, tc.appArmorValidateHostFunc, cmStatusFunc, recordEventFunc)
  1153. // call setter on node
  1154. if err := setter(tc.node); err != nil {
  1155. t.Fatalf("unexpected error: %v", err)
  1156. }
  1157. // check expected condition
  1158. assert.True(t, apiequality.Semantic.DeepEqual(tc.expectConditions, tc.node.Status.Conditions),
  1159. "Diff: %s", diff.ObjectDiff(tc.expectConditions, tc.node.Status.Conditions))
  1160. // check expected events
  1161. require.Equal(t, len(tc.expectEvents), len(events))
  1162. for i := range tc.expectEvents {
  1163. assert.Equal(t, tc.expectEvents[i], events[i])
  1164. }
  1165. })
  1166. }
  1167. }
  1168. func TestMemoryPressureCondition(t *testing.T) {
  1169. now := time.Now()
  1170. before := now.Add(-time.Second)
  1171. nowFunc := func() time.Time { return now }
  1172. cases := []struct {
  1173. desc string
  1174. node *v1.Node
  1175. pressure bool
  1176. expectConditions []v1.NodeCondition
  1177. expectEvents []testEvent
  1178. }{
  1179. {
  1180. desc: "new, no pressure",
  1181. node: &v1.Node{},
  1182. pressure: false,
  1183. expectConditions: []v1.NodeCondition{*makeMemoryPressureCondition(false, now, now)},
  1184. expectEvents: []testEvent{
  1185. {
  1186. eventType: v1.EventTypeNormal,
  1187. event: "NodeHasSufficientMemory",
  1188. },
  1189. },
  1190. },
  1191. {
  1192. desc: "new, pressure",
  1193. node: &v1.Node{},
  1194. pressure: true,
  1195. expectConditions: []v1.NodeCondition{*makeMemoryPressureCondition(true, now, now)},
  1196. expectEvents: []testEvent{
  1197. {
  1198. eventType: v1.EventTypeNormal,
  1199. event: "NodeHasInsufficientMemory",
  1200. },
  1201. },
  1202. },
  1203. {
  1204. desc: "transition to pressure",
  1205. node: &v1.Node{
  1206. Status: v1.NodeStatus{
  1207. Conditions: []v1.NodeCondition{*makeMemoryPressureCondition(false, before, before)},
  1208. },
  1209. },
  1210. pressure: true,
  1211. expectConditions: []v1.NodeCondition{*makeMemoryPressureCondition(true, now, now)},
  1212. expectEvents: []testEvent{
  1213. {
  1214. eventType: v1.EventTypeNormal,
  1215. event: "NodeHasInsufficientMemory",
  1216. },
  1217. },
  1218. },
  1219. {
  1220. desc: "transition to no pressure",
  1221. node: &v1.Node{
  1222. Status: v1.NodeStatus{
  1223. Conditions: []v1.NodeCondition{*makeMemoryPressureCondition(true, before, before)},
  1224. },
  1225. },
  1226. pressure: false,
  1227. expectConditions: []v1.NodeCondition{*makeMemoryPressureCondition(false, now, now)},
  1228. expectEvents: []testEvent{
  1229. {
  1230. eventType: v1.EventTypeNormal,
  1231. event: "NodeHasSufficientMemory",
  1232. },
  1233. },
  1234. },
  1235. {
  1236. desc: "pressure, no transition",
  1237. node: &v1.Node{
  1238. Status: v1.NodeStatus{
  1239. Conditions: []v1.NodeCondition{*makeMemoryPressureCondition(true, before, before)},
  1240. },
  1241. },
  1242. pressure: true,
  1243. expectConditions: []v1.NodeCondition{*makeMemoryPressureCondition(true, before, now)},
  1244. expectEvents: []testEvent{},
  1245. },
  1246. {
  1247. desc: "no pressure, no transition",
  1248. node: &v1.Node{
  1249. Status: v1.NodeStatus{
  1250. Conditions: []v1.NodeCondition{*makeMemoryPressureCondition(false, before, before)},
  1251. },
  1252. },
  1253. pressure: false,
  1254. expectConditions: []v1.NodeCondition{*makeMemoryPressureCondition(false, before, now)},
  1255. expectEvents: []testEvent{},
  1256. },
  1257. }
  1258. for _, tc := range cases {
  1259. t.Run(tc.desc, func(t *testing.T) {
  1260. events := []testEvent{}
  1261. recordEventFunc := func(eventType, event string) {
  1262. events = append(events, testEvent{
  1263. eventType: eventType,
  1264. event: event,
  1265. })
  1266. }
  1267. pressureFunc := func() bool {
  1268. return tc.pressure
  1269. }
  1270. // construct setter
  1271. setter := MemoryPressureCondition(nowFunc, pressureFunc, recordEventFunc)
  1272. // call setter on node
  1273. if err := setter(tc.node); err != nil {
  1274. t.Fatalf("unexpected error: %v", err)
  1275. }
  1276. // check expected condition
  1277. assert.True(t, apiequality.Semantic.DeepEqual(tc.expectConditions, tc.node.Status.Conditions),
  1278. "Diff: %s", diff.ObjectDiff(tc.expectConditions, tc.node.Status.Conditions))
  1279. // check expected events
  1280. require.Equal(t, len(tc.expectEvents), len(events))
  1281. for i := range tc.expectEvents {
  1282. assert.Equal(t, tc.expectEvents[i], events[i])
  1283. }
  1284. })
  1285. }
  1286. }
  1287. func TestPIDPressureCondition(t *testing.T) {
  1288. now := time.Now()
  1289. before := now.Add(-time.Second)
  1290. nowFunc := func() time.Time { return now }
  1291. cases := []struct {
  1292. desc string
  1293. node *v1.Node
  1294. pressure bool
  1295. expectConditions []v1.NodeCondition
  1296. expectEvents []testEvent
  1297. }{
  1298. {
  1299. desc: "new, no pressure",
  1300. node: &v1.Node{},
  1301. pressure: false,
  1302. expectConditions: []v1.NodeCondition{*makePIDPressureCondition(false, now, now)},
  1303. expectEvents: []testEvent{
  1304. {
  1305. eventType: v1.EventTypeNormal,
  1306. event: "NodeHasSufficientPID",
  1307. },
  1308. },
  1309. },
  1310. {
  1311. desc: "new, pressure",
  1312. node: &v1.Node{},
  1313. pressure: true,
  1314. expectConditions: []v1.NodeCondition{*makePIDPressureCondition(true, now, now)},
  1315. expectEvents: []testEvent{
  1316. {
  1317. eventType: v1.EventTypeNormal,
  1318. event: "NodeHasInsufficientPID",
  1319. },
  1320. },
  1321. },
  1322. {
  1323. desc: "transition to pressure",
  1324. node: &v1.Node{
  1325. Status: v1.NodeStatus{
  1326. Conditions: []v1.NodeCondition{*makePIDPressureCondition(false, before, before)},
  1327. },
  1328. },
  1329. pressure: true,
  1330. expectConditions: []v1.NodeCondition{*makePIDPressureCondition(true, now, now)},
  1331. expectEvents: []testEvent{
  1332. {
  1333. eventType: v1.EventTypeNormal,
  1334. event: "NodeHasInsufficientPID",
  1335. },
  1336. },
  1337. },
  1338. {
  1339. desc: "transition to no pressure",
  1340. node: &v1.Node{
  1341. Status: v1.NodeStatus{
  1342. Conditions: []v1.NodeCondition{*makePIDPressureCondition(true, before, before)},
  1343. },
  1344. },
  1345. pressure: false,
  1346. expectConditions: []v1.NodeCondition{*makePIDPressureCondition(false, now, now)},
  1347. expectEvents: []testEvent{
  1348. {
  1349. eventType: v1.EventTypeNormal,
  1350. event: "NodeHasSufficientPID",
  1351. },
  1352. },
  1353. },
  1354. {
  1355. desc: "pressure, no transition",
  1356. node: &v1.Node{
  1357. Status: v1.NodeStatus{
  1358. Conditions: []v1.NodeCondition{*makePIDPressureCondition(true, before, before)},
  1359. },
  1360. },
  1361. pressure: true,
  1362. expectConditions: []v1.NodeCondition{*makePIDPressureCondition(true, before, now)},
  1363. expectEvents: []testEvent{},
  1364. },
  1365. {
  1366. desc: "no pressure, no transition",
  1367. node: &v1.Node{
  1368. Status: v1.NodeStatus{
  1369. Conditions: []v1.NodeCondition{*makePIDPressureCondition(false, before, before)},
  1370. },
  1371. },
  1372. pressure: false,
  1373. expectConditions: []v1.NodeCondition{*makePIDPressureCondition(false, before, now)},
  1374. expectEvents: []testEvent{},
  1375. },
  1376. }
  1377. for _, tc := range cases {
  1378. t.Run(tc.desc, func(t *testing.T) {
  1379. events := []testEvent{}
  1380. recordEventFunc := func(eventType, event string) {
  1381. events = append(events, testEvent{
  1382. eventType: eventType,
  1383. event: event,
  1384. })
  1385. }
  1386. pressureFunc := func() bool {
  1387. return tc.pressure
  1388. }
  1389. // construct setter
  1390. setter := PIDPressureCondition(nowFunc, pressureFunc, recordEventFunc)
  1391. // call setter on node
  1392. if err := setter(tc.node); err != nil {
  1393. t.Fatalf("unexpected error: %v", err)
  1394. }
  1395. // check expected condition
  1396. assert.True(t, apiequality.Semantic.DeepEqual(tc.expectConditions, tc.node.Status.Conditions),
  1397. "Diff: %s", diff.ObjectDiff(tc.expectConditions, tc.node.Status.Conditions))
  1398. // check expected events
  1399. require.Equal(t, len(tc.expectEvents), len(events))
  1400. for i := range tc.expectEvents {
  1401. assert.Equal(t, tc.expectEvents[i], events[i])
  1402. }
  1403. })
  1404. }
  1405. }
  1406. func TestDiskPressureCondition(t *testing.T) {
  1407. now := time.Now()
  1408. before := now.Add(-time.Second)
  1409. nowFunc := func() time.Time { return now }
  1410. cases := []struct {
  1411. desc string
  1412. node *v1.Node
  1413. pressure bool
  1414. expectConditions []v1.NodeCondition
  1415. expectEvents []testEvent
  1416. }{
  1417. {
  1418. desc: "new, no pressure",
  1419. node: &v1.Node{},
  1420. pressure: false,
  1421. expectConditions: []v1.NodeCondition{*makeDiskPressureCondition(false, now, now)},
  1422. expectEvents: []testEvent{
  1423. {
  1424. eventType: v1.EventTypeNormal,
  1425. event: "NodeHasNoDiskPressure",
  1426. },
  1427. },
  1428. },
  1429. {
  1430. desc: "new, pressure",
  1431. node: &v1.Node{},
  1432. pressure: true,
  1433. expectConditions: []v1.NodeCondition{*makeDiskPressureCondition(true, now, now)},
  1434. expectEvents: []testEvent{
  1435. {
  1436. eventType: v1.EventTypeNormal,
  1437. event: "NodeHasDiskPressure",
  1438. },
  1439. },
  1440. },
  1441. {
  1442. desc: "transition to pressure",
  1443. node: &v1.Node{
  1444. Status: v1.NodeStatus{
  1445. Conditions: []v1.NodeCondition{*makeDiskPressureCondition(false, before, before)},
  1446. },
  1447. },
  1448. pressure: true,
  1449. expectConditions: []v1.NodeCondition{*makeDiskPressureCondition(true, now, now)},
  1450. expectEvents: []testEvent{
  1451. {
  1452. eventType: v1.EventTypeNormal,
  1453. event: "NodeHasDiskPressure",
  1454. },
  1455. },
  1456. },
  1457. {
  1458. desc: "transition to no pressure",
  1459. node: &v1.Node{
  1460. Status: v1.NodeStatus{
  1461. Conditions: []v1.NodeCondition{*makeDiskPressureCondition(true, before, before)},
  1462. },
  1463. },
  1464. pressure: false,
  1465. expectConditions: []v1.NodeCondition{*makeDiskPressureCondition(false, now, now)},
  1466. expectEvents: []testEvent{
  1467. {
  1468. eventType: v1.EventTypeNormal,
  1469. event: "NodeHasNoDiskPressure",
  1470. },
  1471. },
  1472. },
  1473. {
  1474. desc: "pressure, no transition",
  1475. node: &v1.Node{
  1476. Status: v1.NodeStatus{
  1477. Conditions: []v1.NodeCondition{*makeDiskPressureCondition(true, before, before)},
  1478. },
  1479. },
  1480. pressure: true,
  1481. expectConditions: []v1.NodeCondition{*makeDiskPressureCondition(true, before, now)},
  1482. expectEvents: []testEvent{},
  1483. },
  1484. {
  1485. desc: "no pressure, no transition",
  1486. node: &v1.Node{
  1487. Status: v1.NodeStatus{
  1488. Conditions: []v1.NodeCondition{*makeDiskPressureCondition(false, before, before)},
  1489. },
  1490. },
  1491. pressure: false,
  1492. expectConditions: []v1.NodeCondition{*makeDiskPressureCondition(false, before, now)},
  1493. expectEvents: []testEvent{},
  1494. },
  1495. }
  1496. for _, tc := range cases {
  1497. t.Run(tc.desc, func(t *testing.T) {
  1498. events := []testEvent{}
  1499. recordEventFunc := func(eventType, event string) {
  1500. events = append(events, testEvent{
  1501. eventType: eventType,
  1502. event: event,
  1503. })
  1504. }
  1505. pressureFunc := func() bool {
  1506. return tc.pressure
  1507. }
  1508. // construct setter
  1509. setter := DiskPressureCondition(nowFunc, pressureFunc, recordEventFunc)
  1510. // call setter on node
  1511. if err := setter(tc.node); err != nil {
  1512. t.Fatalf("unexpected error: %v", err)
  1513. }
  1514. // check expected condition
  1515. assert.True(t, apiequality.Semantic.DeepEqual(tc.expectConditions, tc.node.Status.Conditions),
  1516. "Diff: %s", diff.ObjectDiff(tc.expectConditions, tc.node.Status.Conditions))
  1517. // check expected events
  1518. require.Equal(t, len(tc.expectEvents), len(events))
  1519. for i := range tc.expectEvents {
  1520. assert.Equal(t, tc.expectEvents[i], events[i])
  1521. }
  1522. })
  1523. }
  1524. }
  1525. func TestVolumesInUse(t *testing.T) {
  1526. withVolumesInUse := &v1.Node{
  1527. Status: v1.NodeStatus{
  1528. VolumesInUse: []v1.UniqueVolumeName{"foo"},
  1529. },
  1530. }
  1531. cases := []struct {
  1532. desc string
  1533. node *v1.Node
  1534. synced bool
  1535. volumesInUse []v1.UniqueVolumeName
  1536. expectVolumesInUse []v1.UniqueVolumeName
  1537. }{
  1538. {
  1539. desc: "synced",
  1540. node: withVolumesInUse.DeepCopy(),
  1541. synced: true,
  1542. volumesInUse: []v1.UniqueVolumeName{"bar"},
  1543. expectVolumesInUse: []v1.UniqueVolumeName{"bar"},
  1544. },
  1545. {
  1546. desc: "not synced",
  1547. node: withVolumesInUse.DeepCopy(),
  1548. synced: false,
  1549. volumesInUse: []v1.UniqueVolumeName{"bar"},
  1550. expectVolumesInUse: []v1.UniqueVolumeName{"foo"},
  1551. },
  1552. }
  1553. for _, tc := range cases {
  1554. t.Run(tc.desc, func(t *testing.T) {
  1555. syncedFunc := func() bool {
  1556. return tc.synced
  1557. }
  1558. volumesInUseFunc := func() []v1.UniqueVolumeName {
  1559. return tc.volumesInUse
  1560. }
  1561. // construct setter
  1562. setter := VolumesInUse(syncedFunc, volumesInUseFunc)
  1563. // call setter on node
  1564. if err := setter(tc.node); err != nil {
  1565. t.Fatalf("unexpected error: %v", err)
  1566. }
  1567. // check expected volumes
  1568. assert.True(t, apiequality.Semantic.DeepEqual(tc.expectVolumesInUse, tc.node.Status.VolumesInUse),
  1569. "Diff: %s", diff.ObjectDiff(tc.expectVolumesInUse, tc.node.Status.VolumesInUse))
  1570. })
  1571. }
  1572. }
  1573. func TestVolumeLimits(t *testing.T) {
  1574. const (
  1575. volumeLimitKey = "attachable-volumes-fake-provider"
  1576. volumeLimitVal = 16
  1577. )
  1578. var cases = []struct {
  1579. desc string
  1580. volumePluginList []volume.VolumePluginWithAttachLimits
  1581. expectNode *v1.Node
  1582. }{
  1583. {
  1584. desc: "translate limits to capacity and allocatable for plugins that return successfully from GetVolumeLimits",
  1585. volumePluginList: []volume.VolumePluginWithAttachLimits{
  1586. &volumetest.FakeVolumePlugin{
  1587. VolumeLimits: map[string]int64{volumeLimitKey: volumeLimitVal},
  1588. },
  1589. },
  1590. expectNode: &v1.Node{
  1591. Status: v1.NodeStatus{
  1592. Capacity: v1.ResourceList{
  1593. volumeLimitKey: *resource.NewQuantity(volumeLimitVal, resource.DecimalSI),
  1594. },
  1595. Allocatable: v1.ResourceList{
  1596. volumeLimitKey: *resource.NewQuantity(volumeLimitVal, resource.DecimalSI),
  1597. },
  1598. },
  1599. },
  1600. },
  1601. {
  1602. desc: "skip plugins that return errors from GetVolumeLimits",
  1603. volumePluginList: []volume.VolumePluginWithAttachLimits{
  1604. &volumetest.FakeVolumePlugin{
  1605. VolumeLimitsError: fmt.Errorf("foo"),
  1606. },
  1607. },
  1608. expectNode: &v1.Node{},
  1609. },
  1610. {
  1611. desc: "no plugins",
  1612. expectNode: &v1.Node{},
  1613. },
  1614. }
  1615. for _, tc := range cases {
  1616. t.Run(tc.desc, func(t *testing.T) {
  1617. volumePluginListFunc := func() []volume.VolumePluginWithAttachLimits {
  1618. return tc.volumePluginList
  1619. }
  1620. // construct setter
  1621. setter := VolumeLimits(volumePluginListFunc)
  1622. // call setter on node
  1623. node := &v1.Node{}
  1624. if err := setter(node); err != nil {
  1625. t.Fatalf("unexpected error: %v", err)
  1626. }
  1627. // check expected node
  1628. assert.True(t, apiequality.Semantic.DeepEqual(tc.expectNode, node),
  1629. "Diff: %s", diff.ObjectDiff(tc.expectNode, node))
  1630. })
  1631. }
  1632. }
  1633. // Test Helpers:
  1634. // sortableNodeAddress is a type for sorting []v1.NodeAddress
  1635. type sortableNodeAddress []v1.NodeAddress
  1636. func (s sortableNodeAddress) Len() int { return len(s) }
  1637. func (s sortableNodeAddress) Less(i, j int) bool {
  1638. return (string(s[i].Type) + s[i].Address) < (string(s[j].Type) + s[j].Address)
  1639. }
  1640. func (s sortableNodeAddress) Swap(i, j int) { s[j], s[i] = s[i], s[j] }
  1641. func sortNodeAddresses(addrs sortableNodeAddress) {
  1642. sort.Sort(addrs)
  1643. }
  1644. // testEvent is used to record events for tests
  1645. type testEvent struct {
  1646. eventType string
  1647. event string
  1648. message string
  1649. }
  1650. // makeImageList randomly generates a list of images with the given count
  1651. func makeImageList(numImages, numTags, minSize, maxSize int32) []kubecontainer.Image {
  1652. images := make([]kubecontainer.Image, numImages)
  1653. for i := range images {
  1654. image := &images[i]
  1655. image.ID = string(uuid.NewUUID())
  1656. image.RepoTags = makeImageTags(numTags)
  1657. image.Size = rand.Int63nRange(int64(minSize), int64(maxSize+1))
  1658. }
  1659. return images
  1660. }
  1661. func makeExpectedImageList(imageList []kubecontainer.Image, maxImages, maxNames int32) []v1.ContainerImage {
  1662. // copy the imageList, we do not want to mutate it in-place and accidentally edit a test case
  1663. images := make([]kubecontainer.Image, len(imageList))
  1664. copy(images, imageList)
  1665. // sort images by size
  1666. sort.Sort(sliceutils.ByImageSize(images))
  1667. // convert to []v1.ContainerImage and truncate the list of names
  1668. expectedImages := make([]v1.ContainerImage, len(images))
  1669. for i := range images {
  1670. image := &images[i]
  1671. expectedImage := &expectedImages[i]
  1672. names := append(image.RepoDigests, image.RepoTags...)
  1673. if len(names) > int(maxNames) {
  1674. names = names[0:maxNames]
  1675. }
  1676. expectedImage.Names = names
  1677. expectedImage.SizeBytes = image.Size
  1678. }
  1679. // -1 means no limit, truncate result list if necessary.
  1680. if maxImages > -1 &&
  1681. int(maxImages) < len(expectedImages) {
  1682. return expectedImages[0:maxImages]
  1683. }
  1684. return expectedImages
  1685. }
  1686. func makeImageTags(num int32) []string {
  1687. tags := make([]string, num)
  1688. for i := range tags {
  1689. tags[i] = "k8s.gcr.io:v" + strconv.Itoa(i)
  1690. }
  1691. return tags
  1692. }
  1693. func makeReadyCondition(ready bool, message string, transition, heartbeat time.Time) *v1.NodeCondition {
  1694. if ready {
  1695. return &v1.NodeCondition{
  1696. Type: v1.NodeReady,
  1697. Status: v1.ConditionTrue,
  1698. Reason: "KubeletReady",
  1699. Message: message,
  1700. LastTransitionTime: metav1.NewTime(transition),
  1701. LastHeartbeatTime: metav1.NewTime(heartbeat),
  1702. }
  1703. }
  1704. return &v1.NodeCondition{
  1705. Type: v1.NodeReady,
  1706. Status: v1.ConditionFalse,
  1707. Reason: "KubeletNotReady",
  1708. Message: message,
  1709. LastTransitionTime: metav1.NewTime(transition),
  1710. LastHeartbeatTime: metav1.NewTime(heartbeat),
  1711. }
  1712. }
  1713. func makeMemoryPressureCondition(pressure bool, transition, heartbeat time.Time) *v1.NodeCondition {
  1714. if pressure {
  1715. return &v1.NodeCondition{
  1716. Type: v1.NodeMemoryPressure,
  1717. Status: v1.ConditionTrue,
  1718. Reason: "KubeletHasInsufficientMemory",
  1719. Message: "kubelet has insufficient memory available",
  1720. LastTransitionTime: metav1.NewTime(transition),
  1721. LastHeartbeatTime: metav1.NewTime(heartbeat),
  1722. }
  1723. }
  1724. return &v1.NodeCondition{
  1725. Type: v1.NodeMemoryPressure,
  1726. Status: v1.ConditionFalse,
  1727. Reason: "KubeletHasSufficientMemory",
  1728. Message: "kubelet has sufficient memory available",
  1729. LastTransitionTime: metav1.NewTime(transition),
  1730. LastHeartbeatTime: metav1.NewTime(heartbeat),
  1731. }
  1732. }
  1733. func makePIDPressureCondition(pressure bool, transition, heartbeat time.Time) *v1.NodeCondition {
  1734. if pressure {
  1735. return &v1.NodeCondition{
  1736. Type: v1.NodePIDPressure,
  1737. Status: v1.ConditionTrue,
  1738. Reason: "KubeletHasInsufficientPID",
  1739. Message: "kubelet has insufficient PID available",
  1740. LastTransitionTime: metav1.NewTime(transition),
  1741. LastHeartbeatTime: metav1.NewTime(heartbeat),
  1742. }
  1743. }
  1744. return &v1.NodeCondition{
  1745. Type: v1.NodePIDPressure,
  1746. Status: v1.ConditionFalse,
  1747. Reason: "KubeletHasSufficientPID",
  1748. Message: "kubelet has sufficient PID available",
  1749. LastTransitionTime: metav1.NewTime(transition),
  1750. LastHeartbeatTime: metav1.NewTime(heartbeat),
  1751. }
  1752. }
  1753. func makeDiskPressureCondition(pressure bool, transition, heartbeat time.Time) *v1.NodeCondition {
  1754. if pressure {
  1755. return &v1.NodeCondition{
  1756. Type: v1.NodeDiskPressure,
  1757. Status: v1.ConditionTrue,
  1758. Reason: "KubeletHasDiskPressure",
  1759. Message: "kubelet has disk pressure",
  1760. LastTransitionTime: metav1.NewTime(transition),
  1761. LastHeartbeatTime: metav1.NewTime(heartbeat),
  1762. }
  1763. }
  1764. return &v1.NodeCondition{
  1765. Type: v1.NodeDiskPressure,
  1766. Status: v1.ConditionFalse,
  1767. Reason: "KubeletHasNoDiskPressure",
  1768. Message: "kubelet has no disk pressure",
  1769. LastTransitionTime: metav1.NewTime(transition),
  1770. LastHeartbeatTime: metav1.NewTime(heartbeat),
  1771. }
  1772. }