printers_test.go 109 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844
  1. /*
  2. Copyright 2014 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 internalversion
  14. import (
  15. "bytes"
  16. "encoding/json"
  17. "fmt"
  18. "reflect"
  19. "regexp"
  20. "strconv"
  21. "strings"
  22. "testing"
  23. "time"
  24. "sigs.k8s.io/yaml"
  25. "k8s.io/api/core/v1"
  26. "k8s.io/apimachinery/pkg/api/resource"
  27. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  28. "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
  29. metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
  30. "k8s.io/apimachinery/pkg/runtime"
  31. "k8s.io/apimachinery/pkg/runtime/schema"
  32. yamlserializer "k8s.io/apimachinery/pkg/runtime/serializer/yaml"
  33. "k8s.io/apimachinery/pkg/util/diff"
  34. "k8s.io/apimachinery/pkg/util/intstr"
  35. "k8s.io/apimachinery/pkg/util/sets"
  36. "k8s.io/cli-runtime/pkg/genericclioptions"
  37. genericprinters "k8s.io/cli-runtime/pkg/printers"
  38. "k8s.io/kubernetes/pkg/api/legacyscheme"
  39. "k8s.io/kubernetes/pkg/api/testapi"
  40. "k8s.io/kubernetes/pkg/apis/apps"
  41. "k8s.io/kubernetes/pkg/apis/autoscaling"
  42. "k8s.io/kubernetes/pkg/apis/batch"
  43. "k8s.io/kubernetes/pkg/apis/coordination"
  44. api "k8s.io/kubernetes/pkg/apis/core"
  45. "k8s.io/kubernetes/pkg/apis/networking"
  46. nodeapi "k8s.io/kubernetes/pkg/apis/node"
  47. "k8s.io/kubernetes/pkg/apis/policy"
  48. "k8s.io/kubernetes/pkg/apis/scheduling"
  49. "k8s.io/kubernetes/pkg/apis/storage"
  50. "k8s.io/kubernetes/pkg/printers"
  51. )
  52. var testData = TestStruct{
  53. TypeMeta: metav1.TypeMeta{APIVersion: "foo/bar", Kind: "TestStruct"},
  54. Key: "testValue",
  55. Map: map[string]int{"TestSubkey": 1},
  56. StringList: []string{"a", "b", "c"},
  57. IntList: []int{1, 2, 3},
  58. }
  59. type TestStruct struct {
  60. metav1.TypeMeta `json:",inline"`
  61. metav1.ObjectMeta `json:"metadata,omitempty"`
  62. Key string `json:"Key"`
  63. Map map[string]int `json:"Map"`
  64. StringList []string `json:"StringList"`
  65. IntList []int `json:"IntList"`
  66. }
  67. func (in *TestStruct) DeepCopyObject() runtime.Object {
  68. panic("never called")
  69. }
  70. func TestPrintUnstructuredObject(t *testing.T) {
  71. obj := &unstructured.Unstructured{
  72. Object: map[string]interface{}{
  73. "apiVersion": "v1",
  74. "kind": "Test",
  75. "dummy1": "present",
  76. "dummy2": "present",
  77. "metadata": map[string]interface{}{
  78. "name": "MyName",
  79. "namespace": "MyNamespace",
  80. "creationTimestamp": "2017-04-01T00:00:00Z",
  81. "resourceVersion": 123,
  82. "uid": "00000000-0000-0000-0000-000000000001",
  83. "dummy3": "present",
  84. "labels": map[string]interface{}{"test": "other"},
  85. },
  86. /*"items": []interface{}{
  87. map[string]interface{}{
  88. "itemBool": true,
  89. "itemInt": 42,
  90. },
  91. },*/
  92. "url": "http://localhost",
  93. "status": "ok",
  94. },
  95. }
  96. tests := []struct {
  97. expected string
  98. options printers.PrintOptions
  99. object runtime.Object
  100. }{
  101. {
  102. expected: "NAME\\s+AGE\nMyName\\s+\\d+",
  103. object: obj,
  104. },
  105. {
  106. options: printers.PrintOptions{
  107. WithNamespace: true,
  108. },
  109. expected: "NAMESPACE\\s+NAME\\s+AGE\nMyNamespace\\s+MyName\\s+\\d+",
  110. object: obj,
  111. },
  112. {
  113. options: printers.PrintOptions{
  114. ShowLabels: true,
  115. WithNamespace: true,
  116. },
  117. expected: "NAMESPACE\\s+NAME\\s+AGE\\s+LABELS\nMyNamespace\\s+MyName\\s+\\d+\\w+\\s+test\\=other",
  118. object: obj,
  119. },
  120. {
  121. expected: "NAME\\s+AGE\nMyName\\s+\\d+\\w+\nMyName2\\s+\\d+",
  122. object: &unstructured.Unstructured{
  123. Object: map[string]interface{}{
  124. "apiVersion": "v1",
  125. "kind": "Test",
  126. "dummy1": "present",
  127. "dummy2": "present",
  128. "items": []interface{}{
  129. map[string]interface{}{
  130. "metadata": map[string]interface{}{
  131. "name": "MyName",
  132. "namespace": "MyNamespace",
  133. "creationTimestamp": "2017-04-01T00:00:00Z",
  134. "resourceVersion": 123,
  135. "uid": "00000000-0000-0000-0000-000000000001",
  136. "dummy3": "present",
  137. "labels": map[string]interface{}{"test": "other"},
  138. },
  139. },
  140. map[string]interface{}{
  141. "metadata": map[string]interface{}{
  142. "name": "MyName2",
  143. "namespace": "MyNamespace",
  144. "creationTimestamp": "2017-04-01T00:00:00Z",
  145. "resourceVersion": 123,
  146. "uid": "00000000-0000-0000-0000-000000000001",
  147. "dummy3": "present",
  148. "labels": "badlabel",
  149. },
  150. },
  151. },
  152. "url": "http://localhost",
  153. "status": "ok",
  154. },
  155. },
  156. },
  157. }
  158. out := bytes.NewBuffer([]byte{})
  159. for _, test := range tests {
  160. out.Reset()
  161. printer := printers.NewTablePrinter(test.options)
  162. printer.PrintObj(test.object, out)
  163. matches, err := regexp.MatchString(test.expected, out.String())
  164. if err != nil {
  165. t.Fatalf("unexpected error: %v", err)
  166. }
  167. if !matches {
  168. t.Errorf("wanted:\n%s\ngot:\n%s", test.expected, out)
  169. }
  170. }
  171. }
  172. type TestPrintType struct {
  173. Data string
  174. }
  175. func (obj *TestPrintType) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
  176. func (obj *TestPrintType) DeepCopyObject() runtime.Object {
  177. if obj == nil {
  178. return nil
  179. }
  180. clone := *obj
  181. return &clone
  182. }
  183. type TestUnknownType struct{}
  184. func (obj *TestUnknownType) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
  185. func (obj *TestUnknownType) DeepCopyObject() runtime.Object {
  186. if obj == nil {
  187. return nil
  188. }
  189. clone := *obj
  190. return &clone
  191. }
  192. func testPrinter(t *testing.T, printer printers.ResourcePrinter, unmarshalFunc func(data []byte, v interface{}) error) {
  193. buf := bytes.NewBuffer([]byte{})
  194. err := printer.PrintObj(&testData, buf)
  195. if err != nil {
  196. t.Fatal(err)
  197. }
  198. var poutput TestStruct
  199. // Verify that given function runs without error.
  200. err = unmarshalFunc(buf.Bytes(), &poutput)
  201. if err != nil {
  202. t.Fatal(err)
  203. }
  204. // Use real decode function to undo the versioning process.
  205. poutput = TestStruct{}
  206. s := yamlserializer.NewDecodingSerializer(testapi.Default.Codec())
  207. if err := runtime.DecodeInto(s, buf.Bytes(), &poutput); err != nil {
  208. t.Fatal(err)
  209. }
  210. if !reflect.DeepEqual(testData, poutput) {
  211. t.Errorf("Test data and unmarshaled data are not equal: %v", diff.ObjectDiff(poutput, testData))
  212. }
  213. obj := &v1.Pod{
  214. TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Pod"},
  215. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  216. }
  217. // our decoder defaults, so we should default our expected object as well
  218. legacyscheme.Scheme.Default(obj)
  219. buf.Reset()
  220. printer.PrintObj(obj, buf)
  221. var objOut v1.Pod
  222. // Verify that given function runs without error.
  223. err = unmarshalFunc(buf.Bytes(), &objOut)
  224. if err != nil {
  225. t.Fatalf("unexpected error: %#v", err)
  226. }
  227. // Use real decode function to undo the versioning process.
  228. objOut = v1.Pod{}
  229. if err := runtime.DecodeInto(s, buf.Bytes(), &objOut); err != nil {
  230. t.Fatal(err)
  231. }
  232. if !reflect.DeepEqual(obj, &objOut) {
  233. t.Errorf("Unexpected inequality:\n%v", diff.ObjectDiff(obj, &objOut))
  234. }
  235. }
  236. func yamlUnmarshal(data []byte, v interface{}) error {
  237. return yaml.Unmarshal(data, v)
  238. }
  239. func TestYAMLPrinter(t *testing.T) {
  240. testPrinter(t, genericprinters.NewTypeSetter(legacyscheme.Scheme).ToPrinter(&genericprinters.YAMLPrinter{}), yamlUnmarshal)
  241. }
  242. func TestJSONPrinter(t *testing.T) {
  243. testPrinter(t, genericprinters.NewTypeSetter(legacyscheme.Scheme).ToPrinter(&genericprinters.JSONPrinter{}), json.Unmarshal)
  244. }
  245. func TestFormatResourceName(t *testing.T) {
  246. tests := []struct {
  247. kind schema.GroupKind
  248. name string
  249. want string
  250. }{
  251. {schema.GroupKind{}, "", ""},
  252. {schema.GroupKind{}, "name", "name"},
  253. {schema.GroupKind{Kind: "Kind"}, "", "kind/"}, // should not happen in practice
  254. {schema.GroupKind{Kind: "Kind"}, "name", "kind/name"},
  255. {schema.GroupKind{Group: "group", Kind: "Kind"}, "name", "kind.group/name"},
  256. }
  257. for _, tt := range tests {
  258. if got := formatResourceName(tt.kind, tt.name, true); got != tt.want {
  259. t.Errorf("formatResourceName(%q, %q) = %q, want %q", tt.kind, tt.name, got, tt.want)
  260. }
  261. }
  262. }
  263. func PrintCustomType(obj *TestPrintType, options printers.PrintOptions) ([]metav1beta1.TableRow, error) {
  264. data := obj.Data
  265. kind := options.Kind
  266. if options.WithKind {
  267. data = kind.String() + "/" + data
  268. }
  269. return []metav1beta1.TableRow{{Cells: []interface{}{data}}}, nil
  270. }
  271. func ErrorPrintHandler(obj *TestPrintType, options printers.PrintOptions) ([]metav1beta1.TableRow, error) {
  272. return nil, fmt.Errorf("ErrorPrintHandler error")
  273. }
  274. func TestCustomTypePrinting(t *testing.T) {
  275. columns := []metav1beta1.TableColumnDefinition{{Name: "Data"}}
  276. printer := printers.NewTablePrinter(printers.PrintOptions{})
  277. printer.TableHandler(columns, PrintCustomType)
  278. obj := TestPrintType{"test object"}
  279. buffer := &bytes.Buffer{}
  280. err := printer.PrintObj(&obj, buffer)
  281. if err != nil {
  282. t.Fatalf("An error occurred printing the custom type: %#v", err)
  283. }
  284. expectedOutput := "DATA\ntest object\n"
  285. if buffer.String() != expectedOutput {
  286. t.Errorf("The data was not printed as expected. Expected:\n%s\nGot:\n%s", expectedOutput, buffer.String())
  287. }
  288. }
  289. func TestPrintHandlerError(t *testing.T) {
  290. columns := []metav1beta1.TableColumnDefinition{{Name: "Data"}}
  291. printer := printers.NewTablePrinter(printers.PrintOptions{})
  292. printer.TableHandler(columns, ErrorPrintHandler)
  293. obj := TestPrintType{"test object"}
  294. buffer := &bytes.Buffer{}
  295. err := printer.PrintObj(&obj, buffer)
  296. if err == nil || err.Error() != "ErrorPrintHandler error" {
  297. t.Errorf("Did not get the expected error: %#v", err)
  298. }
  299. }
  300. func TestUnknownTypePrinting(t *testing.T) {
  301. printer := printers.NewTablePrinter(printers.PrintOptions{})
  302. buffer := &bytes.Buffer{}
  303. err := printer.PrintObj(&TestUnknownType{}, buffer)
  304. if err == nil {
  305. t.Errorf("An error was expected from printing unknown type")
  306. }
  307. }
  308. func TestTemplatePanic(t *testing.T) {
  309. tmpl := `{{and ((index .currentState.info "foo").state.running.startedAt) .currentState.info.net.state.running.startedAt}}`
  310. printer, err := genericprinters.NewGoTemplatePrinter([]byte(tmpl))
  311. if err != nil {
  312. t.Fatalf("tmpl fail: %v", err)
  313. }
  314. buffer := &bytes.Buffer{}
  315. err = printer.PrintObj(&v1.Pod{}, buffer)
  316. if err == nil {
  317. t.Fatalf("expected that template to crash")
  318. }
  319. if buffer.String() == "" {
  320. t.Errorf("no debugging info was printed")
  321. }
  322. }
  323. func TestNamePrinter(t *testing.T) {
  324. tests := map[string]struct {
  325. obj runtime.Object
  326. expect string
  327. }{
  328. "singleObject": {
  329. &v1.Pod{
  330. TypeMeta: metav1.TypeMeta{
  331. Kind: "Pod",
  332. },
  333. ObjectMeta: metav1.ObjectMeta{
  334. Name: "foo",
  335. },
  336. },
  337. "pod/foo\n"},
  338. "List": {
  339. &unstructured.UnstructuredList{
  340. Object: map[string]interface{}{
  341. "kind": "List",
  342. "apiVersion": "v1",
  343. },
  344. Items: []unstructured.Unstructured{
  345. {
  346. Object: map[string]interface{}{
  347. "kind": "Pod",
  348. "apiVersion": "v1",
  349. "metadata": map[string]interface{}{
  350. "name": "bar",
  351. },
  352. },
  353. },
  354. },
  355. },
  356. "pod/bar\n"},
  357. }
  358. printFlags := genericclioptions.NewPrintFlags("").WithTypeSetter(legacyscheme.Scheme).WithDefaultOutput("name")
  359. printer, err := printFlags.ToPrinter()
  360. if err != nil {
  361. t.Fatalf("unexpected err: %v", err)
  362. }
  363. for name, item := range tests {
  364. buff := &bytes.Buffer{}
  365. err := printer.PrintObj(item.obj, buff)
  366. if err != nil {
  367. t.Errorf("%v: unexpected err: %v", name, err)
  368. continue
  369. }
  370. got := buff.String()
  371. if item.expect != got {
  372. t.Errorf("%v: expected %v, got %v", name, item.expect, got)
  373. }
  374. }
  375. }
  376. func TestTemplateStrings(t *testing.T) {
  377. // This unit tests the "exists" function as well as the template from update.sh
  378. table := map[string]struct {
  379. pod v1.Pod
  380. expect string
  381. }{
  382. "nilInfo": {v1.Pod{}, "false"},
  383. "emptyInfo": {v1.Pod{Status: v1.PodStatus{ContainerStatuses: []v1.ContainerStatus{}}}, "false"},
  384. "fooExists": {
  385. v1.Pod{
  386. Status: v1.PodStatus{
  387. ContainerStatuses: []v1.ContainerStatus{
  388. {
  389. Name: "foo",
  390. },
  391. },
  392. },
  393. },
  394. "false",
  395. },
  396. "barExists": {
  397. v1.Pod{
  398. Status: v1.PodStatus{
  399. ContainerStatuses: []v1.ContainerStatus{
  400. {
  401. Name: "bar",
  402. },
  403. },
  404. },
  405. },
  406. "false",
  407. },
  408. "bothExist": {
  409. v1.Pod{
  410. Status: v1.PodStatus{
  411. ContainerStatuses: []v1.ContainerStatus{
  412. {
  413. Name: "foo",
  414. },
  415. {
  416. Name: "bar",
  417. },
  418. },
  419. },
  420. },
  421. "false",
  422. },
  423. "barValid": {
  424. v1.Pod{
  425. Status: v1.PodStatus{
  426. ContainerStatuses: []v1.ContainerStatus{
  427. {
  428. Name: "foo",
  429. },
  430. {
  431. Name: "bar",
  432. State: v1.ContainerState{
  433. Running: &v1.ContainerStateRunning{
  434. StartedAt: metav1.Time{},
  435. },
  436. },
  437. },
  438. },
  439. },
  440. },
  441. "false",
  442. },
  443. "bothValid": {
  444. v1.Pod{
  445. Status: v1.PodStatus{
  446. ContainerStatuses: []v1.ContainerStatus{
  447. {
  448. Name: "foo",
  449. State: v1.ContainerState{
  450. Running: &v1.ContainerStateRunning{
  451. StartedAt: metav1.Time{},
  452. },
  453. },
  454. },
  455. {
  456. Name: "bar",
  457. State: v1.ContainerState{
  458. Running: &v1.ContainerStateRunning{
  459. StartedAt: metav1.Time{},
  460. },
  461. },
  462. },
  463. },
  464. },
  465. },
  466. "true",
  467. },
  468. }
  469. // The point of this test is to verify that the below template works.
  470. tmpl := `{{if (exists . "status" "containerStatuses")}}{{range .status.containerStatuses}}{{if (and (eq .name "foo") (exists . "state" "running"))}}true{{end}}{{end}}{{end}}`
  471. printer, err := genericprinters.NewGoTemplatePrinter([]byte(tmpl))
  472. if err != nil {
  473. t.Fatalf("tmpl fail: %v", err)
  474. }
  475. for name, item := range table {
  476. buffer := &bytes.Buffer{}
  477. err = printer.PrintObj(&item.pod, buffer)
  478. if err != nil {
  479. t.Errorf("%v: unexpected err: %v", name, err)
  480. continue
  481. }
  482. actual := buffer.String()
  483. if len(actual) == 0 {
  484. actual = "false"
  485. }
  486. if e := item.expect; e != actual {
  487. t.Errorf("%v: expected %v, got %v", name, e, actual)
  488. }
  489. }
  490. }
  491. func TestPrinters(t *testing.T) {
  492. om := func(name string) metav1.ObjectMeta { return metav1.ObjectMeta{Name: name} }
  493. var (
  494. err error
  495. templatePrinter printers.ResourcePrinter
  496. templatePrinter2 printers.ResourcePrinter
  497. jsonpathPrinter printers.ResourcePrinter
  498. )
  499. templatePrinter, err = genericprinters.NewGoTemplatePrinter([]byte("{{.name}}"))
  500. if err != nil {
  501. t.Fatal(err)
  502. }
  503. templatePrinter2, err = genericprinters.NewGoTemplatePrinter([]byte("{{len .items}}"))
  504. if err != nil {
  505. t.Fatal(err)
  506. }
  507. jsonpathPrinter, err = genericprinters.NewJSONPathPrinter("{.metadata.name}")
  508. if err != nil {
  509. t.Fatal(err)
  510. }
  511. genericPrinters := map[string]printers.ResourcePrinter{
  512. // TODO(juanvallejo): move "generic printer" tests to pkg/kubectl/genericclioptions/printers
  513. "json": genericprinters.NewTypeSetter(legacyscheme.Scheme).ToPrinter(&genericprinters.JSONPrinter{}),
  514. "yaml": genericprinters.NewTypeSetter(legacyscheme.Scheme).ToPrinter(&genericprinters.YAMLPrinter{}),
  515. "template": templatePrinter,
  516. "template2": templatePrinter2,
  517. "jsonpath": jsonpathPrinter,
  518. }
  519. objects := map[string]runtime.Object{
  520. "pod": &v1.Pod{ObjectMeta: om("pod")},
  521. "emptyPodList": &v1.PodList{},
  522. "nonEmptyPodList": &v1.PodList{Items: []v1.Pod{{}}},
  523. "endpoints": &v1.Endpoints{
  524. Subsets: []v1.EndpointSubset{{
  525. Addresses: []v1.EndpointAddress{{IP: "127.0.0.1"}, {IP: "localhost"}},
  526. Ports: []v1.EndpointPort{{Port: 8080}},
  527. }}},
  528. }
  529. // map of printer name to set of objects it should fail on.
  530. expectedErrors := map[string]sets.String{
  531. "template2": sets.NewString("pod", "emptyPodList", "endpoints"),
  532. "jsonpath": sets.NewString("emptyPodList", "nonEmptyPodList", "endpoints"),
  533. }
  534. for pName, p := range genericPrinters {
  535. for oName, obj := range objects {
  536. b := &bytes.Buffer{}
  537. if err := p.PrintObj(obj, b); err != nil {
  538. if set, found := expectedErrors[pName]; found && set.Has(oName) {
  539. // expected error
  540. continue
  541. }
  542. t.Errorf("printer '%v', object '%v'; error: '%v'", pName, oName, err)
  543. }
  544. }
  545. }
  546. // a humanreadable printer deals with internal-versioned objects
  547. humanReadablePrinter := map[string]printers.ResourcePrinter{
  548. "humanReadable": printers.NewTablePrinter(printers.PrintOptions{
  549. NoHeaders: true,
  550. }),
  551. "humanReadableHeaders": printers.NewTablePrinter(printers.PrintOptions{}),
  552. }
  553. AddHandlers((humanReadablePrinter["humanReadable"]).(*printers.HumanReadablePrinter))
  554. AddHandlers((humanReadablePrinter["humanReadableHeaders"]).(*printers.HumanReadablePrinter))
  555. for pName, p := range humanReadablePrinter {
  556. for oName, obj := range objects {
  557. b := &bytes.Buffer{}
  558. if err := p.PrintObj(obj, b); err != nil {
  559. if set, found := expectedErrors[pName]; found && set.Has(oName) {
  560. // expected error
  561. continue
  562. }
  563. t.Errorf("printer '%v', object '%v'; error: '%v'", pName, oName, err)
  564. }
  565. }
  566. }
  567. }
  568. func TestPrintEventsResultSorted(t *testing.T) {
  569. // Arrange
  570. printer := printers.NewTablePrinter(printers.PrintOptions{})
  571. AddHandlers(printer)
  572. obj := api.EventList{
  573. Items: []api.Event{
  574. {
  575. Source: api.EventSource{Component: "kubelet"},
  576. Message: "Item 1",
  577. FirstTimestamp: metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
  578. LastTimestamp: metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
  579. Count: 1,
  580. Type: api.EventTypeNormal,
  581. },
  582. {
  583. Source: api.EventSource{Component: "scheduler"},
  584. Message: "Item 2",
  585. FirstTimestamp: metav1.NewTime(time.Date(1987, time.June, 17, 0, 0, 0, 0, time.UTC)),
  586. LastTimestamp: metav1.NewTime(time.Date(1987, time.June, 17, 0, 0, 0, 0, time.UTC)),
  587. Count: 1,
  588. Type: api.EventTypeNormal,
  589. },
  590. {
  591. Source: api.EventSource{Component: "kubelet"},
  592. Message: "Item 3",
  593. FirstTimestamp: metav1.NewTime(time.Date(2002, time.December, 25, 0, 0, 0, 0, time.UTC)),
  594. LastTimestamp: metav1.NewTime(time.Date(2002, time.December, 25, 0, 0, 0, 0, time.UTC)),
  595. Count: 1,
  596. Type: api.EventTypeNormal,
  597. },
  598. },
  599. }
  600. buffer := &bytes.Buffer{}
  601. // Act
  602. err := printer.PrintObj(&obj, buffer)
  603. // Assert
  604. if err != nil {
  605. t.Fatalf("An error occurred printing the EventList: %#v", err)
  606. }
  607. out := buffer.String()
  608. VerifyDatesInOrder(out, "\n" /* rowDelimiter */, " " /* columnDelimiter */, t)
  609. }
  610. func TestPrintNodeStatus(t *testing.T) {
  611. printer := printers.NewTablePrinter(printers.PrintOptions{})
  612. AddHandlers(printer)
  613. table := []struct {
  614. node api.Node
  615. status string
  616. }{
  617. {
  618. node: api.Node{
  619. ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  620. Status: api.NodeStatus{Conditions: []api.NodeCondition{{Type: api.NodeReady, Status: api.ConditionTrue}}},
  621. },
  622. status: "Ready",
  623. },
  624. {
  625. node: api.Node{
  626. ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
  627. Spec: api.NodeSpec{Unschedulable: true},
  628. Status: api.NodeStatus{Conditions: []api.NodeCondition{{Type: api.NodeReady, Status: api.ConditionTrue}}},
  629. },
  630. status: "Ready,SchedulingDisabled",
  631. },
  632. {
  633. node: api.Node{
  634. ObjectMeta: metav1.ObjectMeta{Name: "foo3"},
  635. Status: api.NodeStatus{Conditions: []api.NodeCondition{
  636. {Type: api.NodeReady, Status: api.ConditionTrue},
  637. {Type: api.NodeReady, Status: api.ConditionTrue}}},
  638. },
  639. status: "Ready",
  640. },
  641. {
  642. node: api.Node{
  643. ObjectMeta: metav1.ObjectMeta{Name: "foo4"},
  644. Status: api.NodeStatus{Conditions: []api.NodeCondition{{Type: api.NodeReady, Status: api.ConditionFalse}}},
  645. },
  646. status: "NotReady",
  647. },
  648. {
  649. node: api.Node{
  650. ObjectMeta: metav1.ObjectMeta{Name: "foo5"},
  651. Spec: api.NodeSpec{Unschedulable: true},
  652. Status: api.NodeStatus{Conditions: []api.NodeCondition{{Type: api.NodeReady, Status: api.ConditionFalse}}},
  653. },
  654. status: "NotReady,SchedulingDisabled",
  655. },
  656. {
  657. node: api.Node{
  658. ObjectMeta: metav1.ObjectMeta{Name: "foo6"},
  659. Status: api.NodeStatus{Conditions: []api.NodeCondition{{Type: "InvalidValue", Status: api.ConditionTrue}}},
  660. },
  661. status: "Unknown",
  662. },
  663. {
  664. node: api.Node{
  665. ObjectMeta: metav1.ObjectMeta{Name: "foo7"},
  666. Status: api.NodeStatus{Conditions: []api.NodeCondition{{}}},
  667. },
  668. status: "Unknown",
  669. },
  670. {
  671. node: api.Node{
  672. ObjectMeta: metav1.ObjectMeta{Name: "foo8"},
  673. Spec: api.NodeSpec{Unschedulable: true},
  674. Status: api.NodeStatus{Conditions: []api.NodeCondition{{Type: "InvalidValue", Status: api.ConditionTrue}}},
  675. },
  676. status: "Unknown,SchedulingDisabled",
  677. },
  678. {
  679. node: api.Node{
  680. ObjectMeta: metav1.ObjectMeta{Name: "foo9"},
  681. Spec: api.NodeSpec{Unschedulable: true},
  682. Status: api.NodeStatus{Conditions: []api.NodeCondition{{}}},
  683. },
  684. status: "Unknown,SchedulingDisabled",
  685. },
  686. }
  687. for _, test := range table {
  688. buffer := &bytes.Buffer{}
  689. err := printer.PrintObj(&test.node, buffer)
  690. if err != nil {
  691. t.Fatalf("An error occurred printing Node: %#v", err)
  692. }
  693. if !contains(strings.Fields(buffer.String()), test.status) {
  694. t.Fatalf("Expect printing node %s with status %#v, got: %#v", test.node.Name, test.status, buffer.String())
  695. }
  696. }
  697. }
  698. func TestPrintNodeRole(t *testing.T) {
  699. printer := printers.NewTablePrinter(printers.PrintOptions{})
  700. AddHandlers(printer)
  701. table := []struct {
  702. node api.Node
  703. expected string
  704. }{
  705. {
  706. node: api.Node{
  707. ObjectMeta: metav1.ObjectMeta{Name: "foo9"},
  708. },
  709. expected: "<none>",
  710. },
  711. {
  712. node: api.Node{
  713. ObjectMeta: metav1.ObjectMeta{
  714. Name: "foo10",
  715. Labels: map[string]string{"node-role.kubernetes.io/master": "", "node-role.kubernetes.io/proxy": "", "kubernetes.io/role": "node"},
  716. },
  717. },
  718. expected: "master,node,proxy",
  719. },
  720. {
  721. node: api.Node{
  722. ObjectMeta: metav1.ObjectMeta{
  723. Name: "foo11",
  724. Labels: map[string]string{"kubernetes.io/role": "node"},
  725. },
  726. },
  727. expected: "node",
  728. },
  729. }
  730. for _, test := range table {
  731. buffer := &bytes.Buffer{}
  732. err := printer.PrintObj(&test.node, buffer)
  733. if err != nil {
  734. t.Fatalf("An error occurred printing Node: %#v", err)
  735. }
  736. if !contains(strings.Fields(buffer.String()), test.expected) {
  737. t.Fatalf("Expect printing node %s with role %#v, got: %#v", test.node.Name, test.expected, buffer.String())
  738. }
  739. }
  740. }
  741. func TestPrintNodeOSImage(t *testing.T) {
  742. printer := printers.NewTablePrinter(printers.PrintOptions{
  743. ColumnLabels: []string{},
  744. Wide: true,
  745. })
  746. AddHandlers(printer)
  747. table := []struct {
  748. node api.Node
  749. osImage string
  750. }{
  751. {
  752. node: api.Node{
  753. ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  754. Status: api.NodeStatus{
  755. NodeInfo: api.NodeSystemInfo{OSImage: "fake-os-image"},
  756. Addresses: []api.NodeAddress{{Type: api.NodeExternalIP, Address: "1.1.1.1"}},
  757. },
  758. },
  759. osImage: "fake-os-image",
  760. },
  761. {
  762. node: api.Node{
  763. ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
  764. Status: api.NodeStatus{
  765. NodeInfo: api.NodeSystemInfo{KernelVersion: "fake-kernel-version"},
  766. Addresses: []api.NodeAddress{{Type: api.NodeExternalIP, Address: "1.1.1.1"}},
  767. },
  768. },
  769. osImage: "<unknown>",
  770. },
  771. }
  772. for _, test := range table {
  773. buffer := &bytes.Buffer{}
  774. err := printer.PrintObj(&test.node, buffer)
  775. if err != nil {
  776. t.Fatalf("An error occurred printing Node: %#v", err)
  777. }
  778. if !contains(strings.Fields(buffer.String()), test.osImage) {
  779. t.Fatalf("Expect printing node %s with os image %#v, got: %#v", test.node.Name, test.osImage, buffer.String())
  780. }
  781. }
  782. }
  783. func TestPrintNodeKernelVersion(t *testing.T) {
  784. printer := printers.NewTablePrinter(printers.PrintOptions{
  785. ColumnLabels: []string{},
  786. Wide: true,
  787. })
  788. AddHandlers(printer)
  789. table := []struct {
  790. node api.Node
  791. kernelVersion string
  792. }{
  793. {
  794. node: api.Node{
  795. ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  796. Status: api.NodeStatus{
  797. NodeInfo: api.NodeSystemInfo{KernelVersion: "fake-kernel-version"},
  798. Addresses: []api.NodeAddress{{Type: api.NodeExternalIP, Address: "1.1.1.1"}},
  799. },
  800. },
  801. kernelVersion: "fake-kernel-version",
  802. },
  803. {
  804. node: api.Node{
  805. ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
  806. Status: api.NodeStatus{
  807. NodeInfo: api.NodeSystemInfo{OSImage: "fake-os-image"},
  808. Addresses: []api.NodeAddress{{Type: api.NodeExternalIP, Address: "1.1.1.1"}},
  809. },
  810. },
  811. kernelVersion: "<unknown>",
  812. },
  813. }
  814. for _, test := range table {
  815. buffer := &bytes.Buffer{}
  816. err := printer.PrintObj(&test.node, buffer)
  817. if err != nil {
  818. t.Fatalf("An error occurred printing Node: %#v", err)
  819. }
  820. if !contains(strings.Fields(buffer.String()), test.kernelVersion) {
  821. t.Fatalf("Expect printing node %s with kernel version %#v, got: %#v", test.node.Name, test.kernelVersion, buffer.String())
  822. }
  823. }
  824. }
  825. func TestPrintNodeContainerRuntimeVersion(t *testing.T) {
  826. printer := printers.NewTablePrinter(printers.PrintOptions{
  827. ColumnLabels: []string{},
  828. Wide: true,
  829. })
  830. AddHandlers(printer)
  831. table := []struct {
  832. node api.Node
  833. containerRuntimeVersion string
  834. }{
  835. {
  836. node: api.Node{
  837. ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  838. Status: api.NodeStatus{
  839. NodeInfo: api.NodeSystemInfo{ContainerRuntimeVersion: "foo://1.2.3"},
  840. Addresses: []api.NodeAddress{{Type: api.NodeExternalIP, Address: "1.1.1.1"}},
  841. },
  842. },
  843. containerRuntimeVersion: "foo://1.2.3",
  844. },
  845. {
  846. node: api.Node{
  847. ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
  848. Status: api.NodeStatus{
  849. NodeInfo: api.NodeSystemInfo{},
  850. Addresses: []api.NodeAddress{{Type: api.NodeExternalIP, Address: "1.1.1.1"}},
  851. },
  852. },
  853. containerRuntimeVersion: "<unknown>",
  854. },
  855. }
  856. for _, test := range table {
  857. buffer := &bytes.Buffer{}
  858. err := printer.PrintObj(&test.node, buffer)
  859. if err != nil {
  860. t.Fatalf("An error occurred printing Node: %#v", err)
  861. }
  862. if !contains(strings.Fields(buffer.String()), test.containerRuntimeVersion) {
  863. t.Fatalf("Expect printing node %s with kernel version %#v, got: %#v", test.node.Name, test.containerRuntimeVersion, buffer.String())
  864. }
  865. }
  866. }
  867. func TestPrintNodeName(t *testing.T) {
  868. printer := printers.NewTablePrinter(printers.PrintOptions{
  869. Wide: true,
  870. })
  871. AddHandlers(printer)
  872. table := []struct {
  873. node api.Node
  874. Name string
  875. }{
  876. {
  877. node: api.Node{
  878. ObjectMeta: metav1.ObjectMeta{Name: "127.0.0.1"},
  879. Status: api.NodeStatus{},
  880. },
  881. Name: "127.0.0.1",
  882. },
  883. {
  884. node: api.Node{
  885. ObjectMeta: metav1.ObjectMeta{Name: ""},
  886. Status: api.NodeStatus{},
  887. },
  888. Name: "<unknown>",
  889. },
  890. }
  891. for _, test := range table {
  892. buffer := &bytes.Buffer{}
  893. err := printer.PrintObj(&test.node, buffer)
  894. if err != nil {
  895. t.Fatalf("An error occurred printing Node: %#v", err)
  896. }
  897. if !contains(strings.Fields(buffer.String()), test.Name) {
  898. t.Fatalf("Expect printing node %s with node name %#v, got: %#v", test.node.Name, test.Name, buffer.String())
  899. }
  900. }
  901. }
  902. func TestPrintNodeExternalIP(t *testing.T) {
  903. printer := printers.NewTablePrinter(printers.PrintOptions{
  904. Wide: true,
  905. })
  906. AddHandlers(printer)
  907. table := []struct {
  908. node api.Node
  909. externalIP string
  910. }{
  911. {
  912. node: api.Node{
  913. ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  914. Status: api.NodeStatus{Addresses: []api.NodeAddress{{Type: api.NodeExternalIP, Address: "1.1.1.1"}}},
  915. },
  916. externalIP: "1.1.1.1",
  917. },
  918. {
  919. node: api.Node{
  920. ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
  921. Status: api.NodeStatus{Addresses: []api.NodeAddress{{Type: api.NodeInternalIP, Address: "1.1.1.1"}}},
  922. },
  923. externalIP: "<none>",
  924. },
  925. {
  926. node: api.Node{
  927. ObjectMeta: metav1.ObjectMeta{Name: "foo3"},
  928. Status: api.NodeStatus{Addresses: []api.NodeAddress{
  929. {Type: api.NodeExternalIP, Address: "2.2.2.2"},
  930. {Type: api.NodeInternalIP, Address: "3.3.3.3"},
  931. {Type: api.NodeExternalIP, Address: "4.4.4.4"},
  932. }},
  933. },
  934. externalIP: "2.2.2.2",
  935. },
  936. }
  937. for _, test := range table {
  938. buffer := &bytes.Buffer{}
  939. err := printer.PrintObj(&test.node, buffer)
  940. if err != nil {
  941. t.Fatalf("An error occurred printing Node: %#v", err)
  942. }
  943. if !contains(strings.Fields(buffer.String()), test.externalIP) {
  944. t.Fatalf("Expect printing node %s with external ip %#v, got: %#v", test.node.Name, test.externalIP, buffer.String())
  945. }
  946. }
  947. }
  948. func TestPrintNodeInternalIP(t *testing.T) {
  949. printer := printers.NewTablePrinter(printers.PrintOptions{
  950. Wide: true,
  951. })
  952. AddHandlers(printer)
  953. table := []struct {
  954. node api.Node
  955. internalIP string
  956. }{
  957. {
  958. node: api.Node{
  959. ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  960. Status: api.NodeStatus{Addresses: []api.NodeAddress{{Type: api.NodeInternalIP, Address: "1.1.1.1"}}},
  961. },
  962. internalIP: "1.1.1.1",
  963. },
  964. {
  965. node: api.Node{
  966. ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
  967. Status: api.NodeStatus{Addresses: []api.NodeAddress{{Type: api.NodeExternalIP, Address: "1.1.1.1"}}},
  968. },
  969. internalIP: "<none>",
  970. },
  971. {
  972. node: api.Node{
  973. ObjectMeta: metav1.ObjectMeta{Name: "foo3"},
  974. Status: api.NodeStatus{Addresses: []api.NodeAddress{
  975. {Type: api.NodeInternalIP, Address: "2.2.2.2"},
  976. {Type: api.NodeExternalIP, Address: "3.3.3.3"},
  977. {Type: api.NodeInternalIP, Address: "4.4.4.4"},
  978. }},
  979. },
  980. internalIP: "2.2.2.2",
  981. },
  982. }
  983. for _, test := range table {
  984. buffer := &bytes.Buffer{}
  985. err := printer.PrintObj(&test.node, buffer)
  986. if err != nil {
  987. t.Fatalf("An error occurred printing Node: %#v", err)
  988. }
  989. if !contains(strings.Fields(buffer.String()), test.internalIP) {
  990. t.Fatalf("Expect printing node %s with internal ip %#v, got: %#v", test.node.Name, test.internalIP, buffer.String())
  991. }
  992. }
  993. }
  994. func contains(fields []string, field string) bool {
  995. for _, v := range fields {
  996. if v == field {
  997. return true
  998. }
  999. }
  1000. return false
  1001. }
  1002. func TestPrintHunmanReadableIngressWithColumnLabels(t *testing.T) {
  1003. ingress := networking.Ingress{
  1004. ObjectMeta: metav1.ObjectMeta{
  1005. Name: "test1",
  1006. CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
  1007. Labels: map[string]string{
  1008. "app_name": "kubectl_test_ingress",
  1009. },
  1010. },
  1011. Spec: networking.IngressSpec{
  1012. Backend: &networking.IngressBackend{
  1013. ServiceName: "svc",
  1014. ServicePort: intstr.FromInt(93),
  1015. },
  1016. },
  1017. Status: networking.IngressStatus{
  1018. LoadBalancer: api.LoadBalancerStatus{
  1019. Ingress: []api.LoadBalancerIngress{
  1020. {
  1021. IP: "2.3.4.5",
  1022. Hostname: "localhost.localdomain",
  1023. },
  1024. },
  1025. },
  1026. },
  1027. }
  1028. buff := bytes.NewBuffer([]byte{})
  1029. table, err := printers.NewTableGenerator().With(AddHandlers).GenerateTable(&ingress, printers.PrintOptions{ColumnLabels: []string{"app_name"}})
  1030. if err != nil {
  1031. t.Fatal(err)
  1032. }
  1033. verifyTable(t, table)
  1034. printer := printers.NewTablePrinter(printers.PrintOptions{NoHeaders: true})
  1035. if err := printer.PrintObj(table, buff); err != nil {
  1036. t.Fatal(err)
  1037. }
  1038. output := string(buff.Bytes())
  1039. appName := ingress.ObjectMeta.Labels["app_name"]
  1040. if !strings.Contains(output, appName) {
  1041. t.Errorf("expected to container app_name label value %s, but doesn't %s", appName, output)
  1042. }
  1043. }
  1044. func TestPrintHumanReadableService(t *testing.T) {
  1045. tests := []api.Service{
  1046. {
  1047. Spec: api.ServiceSpec{
  1048. ClusterIP: "1.2.3.4",
  1049. Type: "LoadBalancer",
  1050. Ports: []api.ServicePort{
  1051. {
  1052. Port: 80,
  1053. Protocol: "TCP",
  1054. },
  1055. },
  1056. },
  1057. Status: api.ServiceStatus{
  1058. LoadBalancer: api.LoadBalancerStatus{
  1059. Ingress: []api.LoadBalancerIngress{
  1060. {
  1061. IP: "2.3.4.5",
  1062. },
  1063. {
  1064. IP: "3.4.5.6",
  1065. },
  1066. },
  1067. },
  1068. },
  1069. },
  1070. {
  1071. Spec: api.ServiceSpec{
  1072. ClusterIP: "1.3.4.5",
  1073. Ports: []api.ServicePort{
  1074. {
  1075. Port: 80,
  1076. Protocol: "TCP",
  1077. },
  1078. {
  1079. Port: 8090,
  1080. Protocol: "UDP",
  1081. },
  1082. {
  1083. Port: 8000,
  1084. Protocol: "TCP",
  1085. },
  1086. {
  1087. Port: 7777,
  1088. Protocol: "SCTP",
  1089. },
  1090. },
  1091. },
  1092. },
  1093. {
  1094. Spec: api.ServiceSpec{
  1095. ClusterIP: "1.4.5.6",
  1096. Type: "LoadBalancer",
  1097. Ports: []api.ServicePort{
  1098. {
  1099. Port: 80,
  1100. Protocol: "TCP",
  1101. },
  1102. {
  1103. Port: 8090,
  1104. Protocol: "UDP",
  1105. },
  1106. {
  1107. Port: 8000,
  1108. Protocol: "TCP",
  1109. },
  1110. },
  1111. },
  1112. Status: api.ServiceStatus{
  1113. LoadBalancer: api.LoadBalancerStatus{
  1114. Ingress: []api.LoadBalancerIngress{
  1115. {
  1116. IP: "2.3.4.5",
  1117. },
  1118. },
  1119. },
  1120. },
  1121. },
  1122. {
  1123. Spec: api.ServiceSpec{
  1124. ClusterIP: "1.5.6.7",
  1125. Type: "LoadBalancer",
  1126. Ports: []api.ServicePort{
  1127. {
  1128. Port: 80,
  1129. Protocol: "TCP",
  1130. },
  1131. {
  1132. Port: 8090,
  1133. Protocol: "UDP",
  1134. },
  1135. {
  1136. Port: 8000,
  1137. Protocol: "TCP",
  1138. },
  1139. },
  1140. },
  1141. Status: api.ServiceStatus{
  1142. LoadBalancer: api.LoadBalancerStatus{
  1143. Ingress: []api.LoadBalancerIngress{
  1144. {
  1145. IP: "2.3.4.5",
  1146. },
  1147. {
  1148. IP: "3.4.5.6",
  1149. },
  1150. {
  1151. IP: "5.6.7.8",
  1152. Hostname: "host5678",
  1153. },
  1154. },
  1155. },
  1156. },
  1157. },
  1158. }
  1159. for _, svc := range tests {
  1160. for _, wide := range []bool{false, true} {
  1161. buff := bytes.NewBuffer([]byte{})
  1162. table, err := printers.NewTableGenerator().With(AddHandlers).GenerateTable(&svc, printers.PrintOptions{Wide: wide})
  1163. if err != nil {
  1164. t.Fatal(err)
  1165. }
  1166. verifyTable(t, table)
  1167. printer := printers.NewTablePrinter(printers.PrintOptions{NoHeaders: true})
  1168. if err := printer.PrintObj(table, buff); err != nil {
  1169. t.Fatal(err)
  1170. }
  1171. output := string(buff.Bytes())
  1172. ip := svc.Spec.ClusterIP
  1173. if !strings.Contains(output, ip) {
  1174. t.Errorf("expected to contain ClusterIP %s, but doesn't: %s", ip, output)
  1175. }
  1176. for n, ingress := range svc.Status.LoadBalancer.Ingress {
  1177. ip = ingress.IP
  1178. // For non-wide output, we only guarantee the first IP to be printed
  1179. if (n == 0 || wide) && !strings.Contains(output, ip) {
  1180. t.Errorf("expected to contain ingress ip %s with wide=%v, but doesn't: %s", ip, wide, output)
  1181. }
  1182. }
  1183. for _, port := range svc.Spec.Ports {
  1184. portSpec := fmt.Sprintf("%d/%s", port.Port, port.Protocol)
  1185. if !strings.Contains(output, portSpec) {
  1186. t.Errorf("expected to contain port: %s, but doesn't: %s", portSpec, output)
  1187. }
  1188. }
  1189. // Each service should print on one line
  1190. if 1 != strings.Count(output, "\n") {
  1191. t.Errorf("expected a single newline, found %d", strings.Count(output, "\n"))
  1192. }
  1193. }
  1194. }
  1195. }
  1196. func TestPrintHumanReadableWithNamespace(t *testing.T) {
  1197. namespaceName := "testnamespace"
  1198. name := "test"
  1199. table := []struct {
  1200. obj runtime.Object
  1201. isNamespaced bool
  1202. }{
  1203. {
  1204. obj: &api.Pod{
  1205. ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespaceName},
  1206. },
  1207. isNamespaced: true,
  1208. },
  1209. {
  1210. obj: &api.ReplicationController{
  1211. ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespaceName},
  1212. Spec: api.ReplicationControllerSpec{
  1213. Replicas: 2,
  1214. Template: &api.PodTemplateSpec{
  1215. ObjectMeta: metav1.ObjectMeta{
  1216. Labels: map[string]string{
  1217. "name": "foo",
  1218. "type": "production",
  1219. },
  1220. },
  1221. Spec: api.PodSpec{
  1222. Containers: []api.Container{
  1223. {
  1224. Image: "foo/bar",
  1225. TerminationMessagePath: api.TerminationMessagePathDefault,
  1226. ImagePullPolicy: api.PullIfNotPresent,
  1227. },
  1228. },
  1229. RestartPolicy: api.RestartPolicyAlways,
  1230. DNSPolicy: api.DNSDefault,
  1231. NodeSelector: map[string]string{
  1232. "baz": "blah",
  1233. },
  1234. },
  1235. },
  1236. },
  1237. },
  1238. isNamespaced: true,
  1239. },
  1240. {
  1241. obj: &api.Service{
  1242. ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespaceName},
  1243. Spec: api.ServiceSpec{
  1244. ClusterIP: "1.2.3.4",
  1245. Ports: []api.ServicePort{
  1246. {
  1247. Port: 80,
  1248. Protocol: "TCP",
  1249. },
  1250. },
  1251. },
  1252. Status: api.ServiceStatus{
  1253. LoadBalancer: api.LoadBalancerStatus{
  1254. Ingress: []api.LoadBalancerIngress{
  1255. {
  1256. IP: "2.3.4.5",
  1257. },
  1258. },
  1259. },
  1260. },
  1261. },
  1262. isNamespaced: true,
  1263. },
  1264. {
  1265. obj: &api.Endpoints{
  1266. ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespaceName},
  1267. Subsets: []api.EndpointSubset{{
  1268. Addresses: []api.EndpointAddress{{IP: "127.0.0.1"}, {IP: "localhost"}},
  1269. Ports: []api.EndpointPort{{Port: 8080}},
  1270. },
  1271. },
  1272. },
  1273. isNamespaced: true,
  1274. },
  1275. {
  1276. obj: &api.Namespace{
  1277. ObjectMeta: metav1.ObjectMeta{Name: name},
  1278. },
  1279. isNamespaced: false,
  1280. },
  1281. {
  1282. obj: &api.Secret{
  1283. ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespaceName},
  1284. },
  1285. isNamespaced: true,
  1286. },
  1287. {
  1288. obj: &api.ServiceAccount{
  1289. ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespaceName},
  1290. Secrets: []api.ObjectReference{},
  1291. },
  1292. isNamespaced: true,
  1293. },
  1294. {
  1295. obj: &api.Node{
  1296. ObjectMeta: metav1.ObjectMeta{Name: name},
  1297. Status: api.NodeStatus{},
  1298. },
  1299. isNamespaced: false,
  1300. },
  1301. {
  1302. obj: &api.PersistentVolume{
  1303. ObjectMeta: metav1.ObjectMeta{Name: name},
  1304. Spec: api.PersistentVolumeSpec{},
  1305. },
  1306. isNamespaced: false,
  1307. },
  1308. {
  1309. obj: &api.PersistentVolumeClaim{
  1310. ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespaceName},
  1311. Spec: api.PersistentVolumeClaimSpec{},
  1312. },
  1313. isNamespaced: true,
  1314. },
  1315. {
  1316. obj: &api.Event{
  1317. ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespaceName},
  1318. Source: api.EventSource{Component: "kubelet"},
  1319. Message: "Item 1",
  1320. FirstTimestamp: metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
  1321. LastTimestamp: metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
  1322. Count: 1,
  1323. Type: api.EventTypeNormal,
  1324. },
  1325. isNamespaced: true,
  1326. },
  1327. {
  1328. obj: &api.LimitRange{
  1329. ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespaceName},
  1330. },
  1331. isNamespaced: true,
  1332. },
  1333. {
  1334. obj: &api.ResourceQuota{
  1335. ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespaceName},
  1336. },
  1337. isNamespaced: true,
  1338. },
  1339. {
  1340. obj: &api.ComponentStatus{
  1341. Conditions: []api.ComponentCondition{
  1342. {Type: api.ComponentHealthy, Status: api.ConditionTrue, Message: "ok", Error: ""},
  1343. },
  1344. },
  1345. isNamespaced: false,
  1346. },
  1347. {
  1348. obj: &unstructured.Unstructured{
  1349. Object: map[string]interface{}{
  1350. "kind": "Foo",
  1351. "apiVersion": "example.com/v1",
  1352. "metadata": map[string]interface{}{"name": "test", "namespace": namespaceName},
  1353. },
  1354. },
  1355. isNamespaced: true,
  1356. },
  1357. {
  1358. obj: &unstructured.Unstructured{
  1359. Object: map[string]interface{}{
  1360. "kind": "Foo",
  1361. "apiVersion": "example.com/v1",
  1362. "metadata": map[string]interface{}{"name": "test"},
  1363. },
  1364. },
  1365. isNamespaced: false,
  1366. },
  1367. }
  1368. for i, test := range table {
  1369. printer := printers.NewTablePrinter(printers.PrintOptions{
  1370. WithNamespace: true,
  1371. NoHeaders: true,
  1372. })
  1373. AddHandlers(printer)
  1374. buffer := &bytes.Buffer{}
  1375. err := printer.PrintObj(test.obj, buffer)
  1376. if err != nil {
  1377. t.Fatalf("An error occurred printing object: %#v", err)
  1378. }
  1379. if test.isNamespaced {
  1380. if !strings.HasPrefix(buffer.String(), namespaceName+" ") {
  1381. t.Errorf("%d: Expect printing object %T to contain namespace %q, got %s", i, test.obj, namespaceName, buffer.String())
  1382. }
  1383. } else {
  1384. if !strings.HasPrefix(buffer.String(), " ") {
  1385. t.Errorf("%d: Expect printing object %T to not contain namespace got %s", i, test.obj, buffer.String())
  1386. }
  1387. }
  1388. }
  1389. }
  1390. func TestPrintPodTable(t *testing.T) {
  1391. runningPod := &api.Pod{
  1392. ObjectMeta: metav1.ObjectMeta{Name: "test1", Labels: map[string]string{"a": "1", "b": "2"}},
  1393. Spec: api.PodSpec{Containers: make([]api.Container, 2)},
  1394. Status: api.PodStatus{
  1395. Phase: "Running",
  1396. ContainerStatuses: []api.ContainerStatus{
  1397. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1398. {RestartCount: 3},
  1399. },
  1400. },
  1401. }
  1402. failedPod := &api.Pod{
  1403. ObjectMeta: metav1.ObjectMeta{Name: "test2", Labels: map[string]string{"b": "2"}},
  1404. Spec: api.PodSpec{Containers: make([]api.Container, 2)},
  1405. Status: api.PodStatus{
  1406. Phase: "Failed",
  1407. ContainerStatuses: []api.ContainerStatus{
  1408. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1409. {RestartCount: 3},
  1410. },
  1411. },
  1412. }
  1413. tests := []struct {
  1414. obj runtime.Object
  1415. opts printers.PrintOptions
  1416. expect string
  1417. ignoreLegacy bool
  1418. }{
  1419. {
  1420. obj: runningPod, opts: printers.PrintOptions{},
  1421. expect: "NAME READY STATUS RESTARTS AGE\ntest1 1/2 Running 6 <unknown>\n",
  1422. },
  1423. {
  1424. obj: runningPod, opts: printers.PrintOptions{WithKind: true, Kind: schema.GroupKind{Kind: "Pod"}},
  1425. expect: "NAME READY STATUS RESTARTS AGE\npod/test1 1/2 Running 6 <unknown>\n",
  1426. },
  1427. {
  1428. obj: runningPod, opts: printers.PrintOptions{ShowLabels: true},
  1429. expect: "NAME READY STATUS RESTARTS AGE LABELS\ntest1 1/2 Running 6 <unknown> a=1,b=2\n",
  1430. },
  1431. {
  1432. obj: &api.PodList{Items: []api.Pod{*runningPod, *failedPod}}, opts: printers.PrintOptions{ColumnLabels: []string{"a"}},
  1433. expect: "NAME READY STATUS RESTARTS AGE A\ntest1 1/2 Running 6 <unknown> 1\ntest2 1/2 Failed 6 <unknown> \n",
  1434. },
  1435. {
  1436. obj: runningPod, opts: printers.PrintOptions{NoHeaders: true},
  1437. expect: "test1 1/2 Running 6 <unknown>\n",
  1438. },
  1439. {
  1440. obj: failedPod, opts: printers.PrintOptions{},
  1441. expect: "NAME READY STATUS RESTARTS AGE\ntest2 1/2 Failed 6 <unknown>\n",
  1442. ignoreLegacy: true, // filtering is not done by the printer in the legacy path
  1443. },
  1444. {
  1445. obj: failedPod, opts: printers.PrintOptions{},
  1446. expect: "NAME READY STATUS RESTARTS AGE\ntest2 1/2 Failed 6 <unknown>\n",
  1447. },
  1448. }
  1449. for i, test := range tests {
  1450. table, err := printers.NewTableGenerator().With(AddHandlers).GenerateTable(test.obj, printers.PrintOptions{})
  1451. if err != nil {
  1452. t.Fatal(err)
  1453. }
  1454. verifyTable(t, table)
  1455. buf := &bytes.Buffer{}
  1456. p := printers.NewTablePrinter(test.opts).With(AddHandlers)
  1457. if err := p.PrintObj(table, buf); err != nil {
  1458. t.Fatal(err)
  1459. }
  1460. if test.expect != buf.String() {
  1461. t.Errorf("%d mismatch:\n%s\n%s", i, strconv.Quote(test.expect), strconv.Quote(buf.String()))
  1462. }
  1463. if test.ignoreLegacy {
  1464. continue
  1465. }
  1466. buf.Reset()
  1467. if err := p.PrintObj(test.obj, buf); err != nil {
  1468. t.Fatal(err)
  1469. }
  1470. if test.expect != buf.String() {
  1471. t.Errorf("%d legacy mismatch:\n%s\n%s", i, strconv.Quote(test.expect), strconv.Quote(buf.String()))
  1472. }
  1473. }
  1474. }
  1475. func TestPrintPod(t *testing.T) {
  1476. tests := []struct {
  1477. pod api.Pod
  1478. expect []metav1beta1.TableRow
  1479. }{
  1480. {
  1481. // Test name, num of containers, restarts, container ready status
  1482. api.Pod{
  1483. ObjectMeta: metav1.ObjectMeta{Name: "test1"},
  1484. Spec: api.PodSpec{Containers: make([]api.Container, 2)},
  1485. Status: api.PodStatus{
  1486. Phase: "podPhase",
  1487. ContainerStatuses: []api.ContainerStatus{
  1488. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1489. {RestartCount: 3},
  1490. },
  1491. },
  1492. },
  1493. []metav1beta1.TableRow{{Cells: []interface{}{"test1", "1/2", "podPhase", int64(6), "<unknown>"}}},
  1494. },
  1495. {
  1496. // Test container error overwrites pod phase
  1497. api.Pod{
  1498. ObjectMeta: metav1.ObjectMeta{Name: "test2"},
  1499. Spec: api.PodSpec{Containers: make([]api.Container, 2)},
  1500. Status: api.PodStatus{
  1501. Phase: "podPhase",
  1502. ContainerStatuses: []api.ContainerStatus{
  1503. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1504. {State: api.ContainerState{Waiting: &api.ContainerStateWaiting{Reason: "ContainerWaitingReason"}}, RestartCount: 3},
  1505. },
  1506. },
  1507. },
  1508. []metav1beta1.TableRow{{Cells: []interface{}{"test2", "1/2", "ContainerWaitingReason", int64(6), "<unknown>"}}},
  1509. },
  1510. {
  1511. // Test the same as the above but with Terminated state and the first container overwrites the rest
  1512. api.Pod{
  1513. ObjectMeta: metav1.ObjectMeta{Name: "test3"},
  1514. Spec: api.PodSpec{Containers: make([]api.Container, 2)},
  1515. Status: api.PodStatus{
  1516. Phase: "podPhase",
  1517. ContainerStatuses: []api.ContainerStatus{
  1518. {State: api.ContainerState{Waiting: &api.ContainerStateWaiting{Reason: "ContainerWaitingReason"}}, RestartCount: 3},
  1519. {State: api.ContainerState{Terminated: &api.ContainerStateTerminated{Reason: "ContainerTerminatedReason"}}, RestartCount: 3},
  1520. },
  1521. },
  1522. },
  1523. []metav1beta1.TableRow{{Cells: []interface{}{"test3", "0/2", "ContainerWaitingReason", int64(6), "<unknown>"}}},
  1524. },
  1525. {
  1526. // Test ready is not enough for reporting running
  1527. api.Pod{
  1528. ObjectMeta: metav1.ObjectMeta{Name: "test4"},
  1529. Spec: api.PodSpec{Containers: make([]api.Container, 2)},
  1530. Status: api.PodStatus{
  1531. Phase: "podPhase",
  1532. ContainerStatuses: []api.ContainerStatus{
  1533. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1534. {Ready: true, RestartCount: 3},
  1535. },
  1536. },
  1537. },
  1538. []metav1beta1.TableRow{{Cells: []interface{}{"test4", "1/2", "podPhase", int64(6), "<unknown>"}}},
  1539. },
  1540. {
  1541. // Test ready is not enough for reporting running
  1542. api.Pod{
  1543. ObjectMeta: metav1.ObjectMeta{Name: "test5"},
  1544. Spec: api.PodSpec{Containers: make([]api.Container, 2)},
  1545. Status: api.PodStatus{
  1546. Reason: "podReason",
  1547. Phase: "podPhase",
  1548. ContainerStatuses: []api.ContainerStatus{
  1549. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1550. {Ready: true, RestartCount: 3},
  1551. },
  1552. },
  1553. },
  1554. []metav1beta1.TableRow{{Cells: []interface{}{"test5", "1/2", "podReason", int64(6), "<unknown>"}}},
  1555. },
  1556. {
  1557. // Test pod has 2 containers, one is running and the other is completed.
  1558. api.Pod{
  1559. ObjectMeta: metav1.ObjectMeta{Name: "test6"},
  1560. Spec: api.PodSpec{Containers: make([]api.Container, 2)},
  1561. Status: api.PodStatus{
  1562. Phase: "Running",
  1563. Reason: "",
  1564. ContainerStatuses: []api.ContainerStatus{
  1565. {Ready: true, RestartCount: 3, State: api.ContainerState{Terminated: &api.ContainerStateTerminated{Reason: "Completed", ExitCode: 0}}},
  1566. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1567. },
  1568. },
  1569. },
  1570. []metav1beta1.TableRow{{Cells: []interface{}{"test6", "1/2", "Running", int64(6), "<unknown>"}}},
  1571. },
  1572. }
  1573. for i, test := range tests {
  1574. rows, err := printPod(&test.pod, printers.PrintOptions{})
  1575. if err != nil {
  1576. t.Fatal(err)
  1577. }
  1578. for i := range rows {
  1579. rows[i].Object.Object = nil
  1580. }
  1581. if !reflect.DeepEqual(test.expect, rows) {
  1582. t.Errorf("%d mismatch: %s", i, diff.ObjectReflectDiff(test.expect, rows))
  1583. }
  1584. }
  1585. }
  1586. func TestPrintPodwide(t *testing.T) {
  1587. condition1 := "condition1"
  1588. condition2 := "condition2"
  1589. condition3 := "condition3"
  1590. tests := []struct {
  1591. pod api.Pod
  1592. expect []metav1beta1.TableRow
  1593. }{
  1594. {
  1595. // Test when the NodeName and PodIP are not none
  1596. api.Pod{
  1597. ObjectMeta: metav1.ObjectMeta{Name: "test1"},
  1598. Spec: api.PodSpec{
  1599. Containers: make([]api.Container, 2),
  1600. NodeName: "test1",
  1601. ReadinessGates: []api.PodReadinessGate{
  1602. {
  1603. ConditionType: api.PodConditionType(condition1),
  1604. },
  1605. {
  1606. ConditionType: api.PodConditionType(condition2),
  1607. },
  1608. {
  1609. ConditionType: api.PodConditionType(condition3),
  1610. },
  1611. },
  1612. },
  1613. Status: api.PodStatus{
  1614. Conditions: []api.PodCondition{
  1615. {
  1616. Type: api.PodConditionType(condition1),
  1617. Status: api.ConditionFalse,
  1618. },
  1619. {
  1620. Type: api.PodConditionType(condition2),
  1621. Status: api.ConditionTrue,
  1622. },
  1623. },
  1624. Phase: "podPhase",
  1625. PodIP: "1.1.1.1",
  1626. ContainerStatuses: []api.ContainerStatus{
  1627. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1628. {RestartCount: 3},
  1629. },
  1630. NominatedNodeName: "node1",
  1631. },
  1632. },
  1633. []metav1beta1.TableRow{{Cells: []interface{}{"test1", "1/2", "podPhase", int64(6), "<unknown>", "1.1.1.1", "test1", "node1", "1/3"}}},
  1634. },
  1635. {
  1636. // Test when the NodeName and PodIP are none
  1637. api.Pod{
  1638. ObjectMeta: metav1.ObjectMeta{Name: "test2"},
  1639. Spec: api.PodSpec{
  1640. Containers: make([]api.Container, 2),
  1641. NodeName: "",
  1642. },
  1643. Status: api.PodStatus{
  1644. Phase: "podPhase",
  1645. PodIP: "",
  1646. ContainerStatuses: []api.ContainerStatus{
  1647. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1648. {State: api.ContainerState{Waiting: &api.ContainerStateWaiting{Reason: "ContainerWaitingReason"}}, RestartCount: 3},
  1649. },
  1650. },
  1651. },
  1652. []metav1beta1.TableRow{{Cells: []interface{}{"test2", "1/2", "ContainerWaitingReason", int64(6), "<unknown>", "<none>", "<none>", "<none>", "<none>"}}},
  1653. },
  1654. }
  1655. for i, test := range tests {
  1656. rows, err := printPod(&test.pod, printers.PrintOptions{Wide: true})
  1657. if err != nil {
  1658. t.Fatal(err)
  1659. }
  1660. for i := range rows {
  1661. rows[i].Object.Object = nil
  1662. }
  1663. if !reflect.DeepEqual(test.expect, rows) {
  1664. t.Errorf("%d mismatch: %s", i, diff.ObjectReflectDiff(test.expect, rows))
  1665. }
  1666. }
  1667. }
  1668. func TestPrintPodList(t *testing.T) {
  1669. tests := []struct {
  1670. pods api.PodList
  1671. expect []metav1beta1.TableRow
  1672. }{
  1673. // Test podList's pod: name, num of containers, restarts, container ready status
  1674. {
  1675. api.PodList{
  1676. Items: []api.Pod{
  1677. {
  1678. ObjectMeta: metav1.ObjectMeta{Name: "test1"},
  1679. Spec: api.PodSpec{Containers: make([]api.Container, 2)},
  1680. Status: api.PodStatus{
  1681. Phase: "podPhase",
  1682. ContainerStatuses: []api.ContainerStatus{
  1683. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1684. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1685. },
  1686. },
  1687. },
  1688. {
  1689. ObjectMeta: metav1.ObjectMeta{Name: "test2"},
  1690. Spec: api.PodSpec{Containers: make([]api.Container, 1)},
  1691. Status: api.PodStatus{
  1692. Phase: "podPhase",
  1693. ContainerStatuses: []api.ContainerStatus{
  1694. {Ready: true, RestartCount: 1, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1695. },
  1696. },
  1697. },
  1698. },
  1699. },
  1700. []metav1beta1.TableRow{{Cells: []interface{}{"test1", "2/2", "podPhase", int64(6), "<unknown>"}}, {Cells: []interface{}{"test2", "1/1", "podPhase", int64(1), "<unknown>"}}},
  1701. },
  1702. }
  1703. for _, test := range tests {
  1704. rows, err := printPodList(&test.pods, printers.PrintOptions{})
  1705. if err != nil {
  1706. t.Fatal(err)
  1707. }
  1708. for i := range rows {
  1709. rows[i].Object.Object = nil
  1710. }
  1711. if !reflect.DeepEqual(test.expect, rows) {
  1712. t.Errorf("mismatch: %s", diff.ObjectReflectDiff(test.expect, rows))
  1713. }
  1714. }
  1715. }
  1716. func TestPrintNonTerminatedPod(t *testing.T) {
  1717. tests := []struct {
  1718. pod api.Pod
  1719. expect []metav1beta1.TableRow
  1720. }{
  1721. {
  1722. // Test pod phase Running should be printed
  1723. api.Pod{
  1724. ObjectMeta: metav1.ObjectMeta{Name: "test1"},
  1725. Spec: api.PodSpec{Containers: make([]api.Container, 2)},
  1726. Status: api.PodStatus{
  1727. Phase: api.PodRunning,
  1728. ContainerStatuses: []api.ContainerStatus{
  1729. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1730. {RestartCount: 3},
  1731. },
  1732. },
  1733. },
  1734. []metav1beta1.TableRow{{Cells: []interface{}{"test1", "1/2", "Running", int64(6), "<unknown>"}}},
  1735. },
  1736. {
  1737. // Test pod phase Pending should be printed
  1738. api.Pod{
  1739. ObjectMeta: metav1.ObjectMeta{Name: "test2"},
  1740. Spec: api.PodSpec{Containers: make([]api.Container, 2)},
  1741. Status: api.PodStatus{
  1742. Phase: api.PodPending,
  1743. ContainerStatuses: []api.ContainerStatus{
  1744. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1745. {RestartCount: 3},
  1746. },
  1747. },
  1748. },
  1749. []metav1beta1.TableRow{{Cells: []interface{}{"test2", "1/2", "Pending", int64(6), "<unknown>"}}},
  1750. },
  1751. {
  1752. // Test pod phase Unknown should be printed
  1753. api.Pod{
  1754. ObjectMeta: metav1.ObjectMeta{Name: "test3"},
  1755. Spec: api.PodSpec{Containers: make([]api.Container, 2)},
  1756. Status: api.PodStatus{
  1757. Phase: api.PodUnknown,
  1758. ContainerStatuses: []api.ContainerStatus{
  1759. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1760. {RestartCount: 3},
  1761. },
  1762. },
  1763. },
  1764. []metav1beta1.TableRow{{Cells: []interface{}{"test3", "1/2", "Unknown", int64(6), "<unknown>"}}},
  1765. },
  1766. {
  1767. // Test pod phase Succeeded shouldn't be printed
  1768. api.Pod{
  1769. ObjectMeta: metav1.ObjectMeta{Name: "test4"},
  1770. Spec: api.PodSpec{Containers: make([]api.Container, 2)},
  1771. Status: api.PodStatus{
  1772. Phase: api.PodSucceeded,
  1773. ContainerStatuses: []api.ContainerStatus{
  1774. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1775. {RestartCount: 3},
  1776. },
  1777. },
  1778. },
  1779. []metav1beta1.TableRow{{Cells: []interface{}{"test4", "1/2", "Succeeded", int64(6), "<unknown>"}, Conditions: podSuccessConditions}},
  1780. },
  1781. {
  1782. // Test pod phase Failed shouldn't be printed
  1783. api.Pod{
  1784. ObjectMeta: metav1.ObjectMeta{Name: "test5"},
  1785. Spec: api.PodSpec{Containers: make([]api.Container, 2)},
  1786. Status: api.PodStatus{
  1787. Phase: api.PodFailed,
  1788. ContainerStatuses: []api.ContainerStatus{
  1789. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1790. {Ready: true, RestartCount: 3},
  1791. },
  1792. },
  1793. },
  1794. []metav1beta1.TableRow{{Cells: []interface{}{"test5", "1/2", "Failed", int64(6), "<unknown>"}, Conditions: podFailedConditions}},
  1795. },
  1796. }
  1797. for i, test := range tests {
  1798. table, err := printers.NewTableGenerator().With(AddHandlers).GenerateTable(&test.pod, printers.PrintOptions{})
  1799. if err != nil {
  1800. t.Fatal(err)
  1801. }
  1802. verifyTable(t, table)
  1803. rows := table.Rows
  1804. for i := range rows {
  1805. rows[i].Object.Object = nil
  1806. }
  1807. if !reflect.DeepEqual(test.expect, rows) {
  1808. t.Errorf("%d mismatch: %s", i, diff.ObjectReflectDiff(test.expect, rows))
  1809. }
  1810. }
  1811. }
  1812. func TestPrintPodWithLabels(t *testing.T) {
  1813. tests := []struct {
  1814. pod api.Pod
  1815. labelColumns []string
  1816. expect []metav1beta1.TableRow
  1817. }{
  1818. {
  1819. // Test name, num of containers, restarts, container ready status
  1820. api.Pod{
  1821. ObjectMeta: metav1.ObjectMeta{
  1822. Name: "test1",
  1823. Labels: map[string]string{"col1": "asd", "COL2": "zxc"},
  1824. },
  1825. Spec: api.PodSpec{Containers: make([]api.Container, 2)},
  1826. Status: api.PodStatus{
  1827. Phase: "podPhase",
  1828. ContainerStatuses: []api.ContainerStatus{
  1829. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1830. {RestartCount: 3},
  1831. },
  1832. },
  1833. },
  1834. []string{"col1", "COL2"},
  1835. []metav1beta1.TableRow{{Cells: []interface{}{"test1", "1/2", "podPhase", int64(6), "<unknown>", "asd", "zxc"}}},
  1836. },
  1837. {
  1838. // Test name, num of containers, restarts, container ready status
  1839. api.Pod{
  1840. ObjectMeta: metav1.ObjectMeta{
  1841. Name: "test1",
  1842. Labels: map[string]string{"col1": "asd", "COL2": "zxc"},
  1843. },
  1844. Spec: api.PodSpec{Containers: make([]api.Container, 2)},
  1845. Status: api.PodStatus{
  1846. Phase: "podPhase",
  1847. ContainerStatuses: []api.ContainerStatus{
  1848. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1849. {RestartCount: 3},
  1850. },
  1851. },
  1852. },
  1853. []string{},
  1854. []metav1beta1.TableRow{{Cells: []interface{}{"test1", "1/2", "podPhase", int64(6), "<unknown>"}}},
  1855. },
  1856. }
  1857. for i, test := range tests {
  1858. table, err := printers.NewTableGenerator().With(AddHandlers).GenerateTable(&test.pod, printers.PrintOptions{ColumnLabels: test.labelColumns})
  1859. if err != nil {
  1860. t.Fatal(err)
  1861. }
  1862. verifyTable(t, table)
  1863. rows := table.Rows
  1864. for i := range rows {
  1865. rows[i].Object.Object = nil
  1866. }
  1867. if !reflect.DeepEqual(test.expect, rows) {
  1868. t.Errorf("%d mismatch: %s", i, diff.ObjectReflectDiff(test.expect, rows))
  1869. }
  1870. }
  1871. }
  1872. type stringTestList []struct {
  1873. name, got, exp string
  1874. }
  1875. func TestTranslateTimestampSince(t *testing.T) {
  1876. tl := stringTestList{
  1877. {"a while from now", translateTimestampSince(metav1.Time{Time: time.Now().Add(2.1e9)}), "<invalid>"},
  1878. {"almost now", translateTimestampSince(metav1.Time{Time: time.Now().Add(1.9e9)}), "0s"},
  1879. {"now", translateTimestampSince(metav1.Time{Time: time.Now()}), "0s"},
  1880. {"unknown", translateTimestampSince(metav1.Time{}), "<unknown>"},
  1881. {"30 seconds ago", translateTimestampSince(metav1.Time{Time: time.Now().Add(-3e10)}), "30s"},
  1882. {"5 minutes ago", translateTimestampSince(metav1.Time{Time: time.Now().Add(-3e11)}), "5m"},
  1883. {"an hour ago", translateTimestampSince(metav1.Time{Time: time.Now().Add(-6e12)}), "100m"},
  1884. {"2 days ago", translateTimestampSince(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, -2)}), "2d"},
  1885. {"months ago", translateTimestampSince(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, -90)}), "90d"},
  1886. {"10 years ago", translateTimestampSince(metav1.Time{Time: time.Now().UTC().AddDate(-10, 0, 0)}), "10y"},
  1887. }
  1888. for _, test := range tl {
  1889. if test.got != test.exp {
  1890. t.Errorf("On %v, expected '%v', but got '%v'",
  1891. test.name, test.exp, test.got)
  1892. }
  1893. }
  1894. }
  1895. func TestTranslateTimestampUntil(t *testing.T) {
  1896. // Since this method compares the time with time.Now() internally,
  1897. // small buffers of 0.1 seconds are added on comparing times to consider method call overhead.
  1898. // Otherwise, the output strings become shorter than expected.
  1899. const buf = 1e8
  1900. tl := stringTestList{
  1901. {"a while ago", translateTimestampUntil(metav1.Time{Time: time.Now().Add(-2.1e9)}), "<invalid>"},
  1902. {"almost now", translateTimestampUntil(metav1.Time{Time: time.Now().Add(-1.9e9)}), "0s"},
  1903. {"now", translateTimestampUntil(metav1.Time{Time: time.Now()}), "0s"},
  1904. {"unknown", translateTimestampUntil(metav1.Time{}), "<unknown>"},
  1905. {"in 30 seconds", translateTimestampUntil(metav1.Time{Time: time.Now().Add(3e10 + buf)}), "30s"},
  1906. {"in 5 minutes", translateTimestampUntil(metav1.Time{Time: time.Now().Add(3e11 + buf)}), "5m"},
  1907. {"in an hour", translateTimestampUntil(metav1.Time{Time: time.Now().Add(6e12 + buf)}), "100m"},
  1908. {"in 2 days", translateTimestampUntil(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, 2).Add(buf)}), "2d"},
  1909. {"in months", translateTimestampUntil(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, 90).Add(buf)}), "90d"},
  1910. {"in 10 years", translateTimestampUntil(metav1.Time{Time: time.Now().UTC().AddDate(10, 0, 0).Add(buf)}), "10y"},
  1911. }
  1912. for _, test := range tl {
  1913. if test.got != test.exp {
  1914. t.Errorf("On %v, expected '%v', but got '%v'",
  1915. test.name, test.exp, test.got)
  1916. }
  1917. }
  1918. }
  1919. func TestPrintDeployment(t *testing.T) {
  1920. tests := []struct {
  1921. deployment apps.Deployment
  1922. expect string
  1923. wideExpect string
  1924. }{
  1925. {
  1926. apps.Deployment{
  1927. ObjectMeta: metav1.ObjectMeta{
  1928. Name: "test1",
  1929. CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  1930. },
  1931. Spec: apps.DeploymentSpec{
  1932. Replicas: 5,
  1933. Template: api.PodTemplateSpec{
  1934. Spec: api.PodSpec{
  1935. Containers: []api.Container{
  1936. {
  1937. Name: "fake-container1",
  1938. Image: "fake-image1",
  1939. },
  1940. {
  1941. Name: "fake-container2",
  1942. Image: "fake-image2",
  1943. },
  1944. },
  1945. },
  1946. },
  1947. Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  1948. },
  1949. Status: apps.DeploymentStatus{
  1950. Replicas: 10,
  1951. UpdatedReplicas: 2,
  1952. AvailableReplicas: 1,
  1953. UnavailableReplicas: 4,
  1954. },
  1955. },
  1956. "test1 0/5 2 1 0s\n",
  1957. "test1 0/5 2 1 0s fake-container1,fake-container2 fake-image1,fake-image2 foo=bar\n",
  1958. },
  1959. }
  1960. buf := bytes.NewBuffer([]byte{})
  1961. for _, test := range tests {
  1962. table, err := printers.NewTableGenerator().With(AddHandlers).GenerateTable(&test.deployment, printers.PrintOptions{})
  1963. if err != nil {
  1964. t.Fatal(err)
  1965. }
  1966. verifyTable(t, table)
  1967. printer := printers.NewTablePrinter(printers.PrintOptions{NoHeaders: true})
  1968. if err := printer.PrintObj(table, buf); err != nil {
  1969. t.Fatal(err)
  1970. }
  1971. if buf.String() != test.expect {
  1972. t.Fatalf("Expected: %s, got: %s", test.expect, buf.String())
  1973. }
  1974. buf.Reset()
  1975. table, err = printers.NewTableGenerator().With(AddHandlers).GenerateTable(&test.deployment, printers.PrintOptions{Wide: true})
  1976. verifyTable(t, table)
  1977. // print deployment with '-o wide' option
  1978. printer = printers.NewTablePrinter(printers.PrintOptions{Wide: true, NoHeaders: true})
  1979. if err := printer.PrintObj(table, buf); err != nil {
  1980. t.Fatal(err)
  1981. }
  1982. if buf.String() != test.wideExpect {
  1983. t.Fatalf("Expected: %s, got: %s", test.wideExpect, buf.String())
  1984. }
  1985. buf.Reset()
  1986. }
  1987. }
  1988. func TestPrintDaemonSet(t *testing.T) {
  1989. tests := []struct {
  1990. ds apps.DaemonSet
  1991. startsWith string
  1992. }{
  1993. {
  1994. apps.DaemonSet{
  1995. ObjectMeta: metav1.ObjectMeta{
  1996. Name: "test1",
  1997. CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  1998. },
  1999. Spec: apps.DaemonSetSpec{
  2000. Template: api.PodTemplateSpec{
  2001. Spec: api.PodSpec{Containers: make([]api.Container, 2)},
  2002. },
  2003. },
  2004. Status: apps.DaemonSetStatus{
  2005. CurrentNumberScheduled: 2,
  2006. DesiredNumberScheduled: 3,
  2007. NumberReady: 1,
  2008. UpdatedNumberScheduled: 2,
  2009. NumberAvailable: 0,
  2010. },
  2011. },
  2012. "test1 3 2 1 2 0 <none> 0s",
  2013. },
  2014. }
  2015. buf := bytes.NewBuffer([]byte{})
  2016. for _, test := range tests {
  2017. table, err := printers.NewTableGenerator().With(AddHandlers).GenerateTable(&test.ds, printers.PrintOptions{})
  2018. if err != nil {
  2019. t.Fatal(err)
  2020. }
  2021. verifyTable(t, table)
  2022. printer := printers.NewTablePrinter(printers.PrintOptions{NoHeaders: true})
  2023. if err := printer.PrintObj(table, buf); err != nil {
  2024. t.Fatal(err)
  2025. }
  2026. if !strings.HasPrefix(buf.String(), test.startsWith) {
  2027. t.Fatalf("Expected to start with %s but got %s", test.startsWith, buf.String())
  2028. }
  2029. buf.Reset()
  2030. }
  2031. }
  2032. func TestPrintJob(t *testing.T) {
  2033. now := time.Now()
  2034. completions := int32(2)
  2035. tests := []struct {
  2036. job batch.Job
  2037. expect string
  2038. }{
  2039. {
  2040. batch.Job{
  2041. ObjectMeta: metav1.ObjectMeta{
  2042. Name: "job1",
  2043. CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  2044. },
  2045. Spec: batch.JobSpec{
  2046. Completions: &completions,
  2047. },
  2048. Status: batch.JobStatus{
  2049. Succeeded: 1,
  2050. },
  2051. },
  2052. "job1 1/2 0s\n",
  2053. },
  2054. {
  2055. batch.Job{
  2056. ObjectMeta: metav1.ObjectMeta{
  2057. Name: "job2",
  2058. CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
  2059. },
  2060. Spec: batch.JobSpec{
  2061. Completions: nil,
  2062. },
  2063. Status: batch.JobStatus{
  2064. Succeeded: 0,
  2065. },
  2066. },
  2067. "job2 0/1 10y\n",
  2068. },
  2069. {
  2070. batch.Job{
  2071. ObjectMeta: metav1.ObjectMeta{
  2072. Name: "job3",
  2073. CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
  2074. },
  2075. Spec: batch.JobSpec{
  2076. Completions: nil,
  2077. },
  2078. Status: batch.JobStatus{
  2079. Succeeded: 0,
  2080. StartTime: &metav1.Time{Time: now.Add(time.Minute)},
  2081. CompletionTime: &metav1.Time{Time: now.Add(31 * time.Minute)},
  2082. },
  2083. },
  2084. "job3 0/1 30m 10y\n",
  2085. },
  2086. {
  2087. batch.Job{
  2088. ObjectMeta: metav1.ObjectMeta{
  2089. Name: "job4",
  2090. CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
  2091. },
  2092. Spec: batch.JobSpec{
  2093. Completions: nil,
  2094. },
  2095. Status: batch.JobStatus{
  2096. Succeeded: 0,
  2097. StartTime: &metav1.Time{Time: time.Now().Add(-20 * time.Minute)},
  2098. },
  2099. },
  2100. "job4 0/1 20m 10y\n",
  2101. },
  2102. }
  2103. buf := bytes.NewBuffer([]byte{})
  2104. for _, test := range tests {
  2105. table, err := printers.NewTableGenerator().With(AddHandlers).GenerateTable(&test.job, printers.PrintOptions{})
  2106. if err != nil {
  2107. t.Fatal(err)
  2108. }
  2109. verifyTable(t, table)
  2110. printer := printers.NewTablePrinter(printers.PrintOptions{NoHeaders: true})
  2111. if err := printer.PrintObj(table, buf); err != nil {
  2112. t.Fatal(err)
  2113. }
  2114. if buf.String() != test.expect {
  2115. t.Errorf("Expected: %s, got: %s", test.expect, buf.String())
  2116. }
  2117. buf.Reset()
  2118. }
  2119. }
  2120. func TestPrintHPA(t *testing.T) {
  2121. minReplicasVal := int32(2)
  2122. targetUtilizationVal := int32(80)
  2123. currentUtilizationVal := int32(50)
  2124. metricLabelSelector, err := metav1.ParseToLabelSelector("label=value")
  2125. if err != nil {
  2126. t.Errorf("unable to parse label selector: %v", err)
  2127. }
  2128. tests := []struct {
  2129. hpa autoscaling.HorizontalPodAutoscaler
  2130. expected string
  2131. }{
  2132. // minReplicas unset
  2133. {
  2134. autoscaling.HorizontalPodAutoscaler{
  2135. ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  2136. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  2137. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  2138. Name: "some-rc",
  2139. Kind: "ReplicationController",
  2140. },
  2141. MaxReplicas: 10,
  2142. },
  2143. Status: autoscaling.HorizontalPodAutoscalerStatus{
  2144. CurrentReplicas: 4,
  2145. DesiredReplicas: 5,
  2146. },
  2147. },
  2148. "some-hpa ReplicationController/some-rc <none> <unset> 10 4 <unknown>\n",
  2149. },
  2150. // external source type, target average value (no current)
  2151. {
  2152. autoscaling.HorizontalPodAutoscaler{
  2153. ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  2154. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  2155. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  2156. Name: "some-rc",
  2157. Kind: "ReplicationController",
  2158. },
  2159. MinReplicas: &minReplicasVal,
  2160. MaxReplicas: 10,
  2161. Metrics: []autoscaling.MetricSpec{
  2162. {
  2163. Type: autoscaling.ExternalMetricSourceType,
  2164. External: &autoscaling.ExternalMetricSource{
  2165. Metric: autoscaling.MetricIdentifier{
  2166. Name: "some-external-metric",
  2167. Selector: metricLabelSelector,
  2168. },
  2169. Target: autoscaling.MetricTarget{
  2170. Type: autoscaling.AverageValueMetricType,
  2171. AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  2172. },
  2173. },
  2174. },
  2175. },
  2176. },
  2177. Status: autoscaling.HorizontalPodAutoscalerStatus{
  2178. CurrentReplicas: 4,
  2179. DesiredReplicas: 5,
  2180. },
  2181. },
  2182. "some-hpa ReplicationController/some-rc <unknown>/100m (avg) 2 10 4 <unknown>\n",
  2183. },
  2184. // external source type, target average value
  2185. {
  2186. autoscaling.HorizontalPodAutoscaler{
  2187. ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  2188. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  2189. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  2190. Name: "some-rc",
  2191. Kind: "ReplicationController",
  2192. },
  2193. MinReplicas: &minReplicasVal,
  2194. MaxReplicas: 10,
  2195. Metrics: []autoscaling.MetricSpec{
  2196. {
  2197. Type: autoscaling.ExternalMetricSourceType,
  2198. External: &autoscaling.ExternalMetricSource{
  2199. Metric: autoscaling.MetricIdentifier{
  2200. Name: "some-external-metric",
  2201. Selector: metricLabelSelector,
  2202. },
  2203. Target: autoscaling.MetricTarget{
  2204. Type: autoscaling.AverageValueMetricType,
  2205. AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  2206. },
  2207. },
  2208. },
  2209. },
  2210. },
  2211. Status: autoscaling.HorizontalPodAutoscalerStatus{
  2212. CurrentReplicas: 4,
  2213. DesiredReplicas: 5,
  2214. CurrentMetrics: []autoscaling.MetricStatus{
  2215. {
  2216. Type: autoscaling.ExternalMetricSourceType,
  2217. External: &autoscaling.ExternalMetricStatus{
  2218. Metric: autoscaling.MetricIdentifier{
  2219. Name: "some-external-metric",
  2220. Selector: metricLabelSelector,
  2221. },
  2222. Current: autoscaling.MetricValueStatus{
  2223. AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
  2224. },
  2225. },
  2226. },
  2227. },
  2228. },
  2229. },
  2230. "some-hpa ReplicationController/some-rc 50m/100m (avg) 2 10 4 <unknown>\n",
  2231. },
  2232. // external source type, target value (no current)
  2233. {
  2234. autoscaling.HorizontalPodAutoscaler{
  2235. ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  2236. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  2237. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  2238. Name: "some-rc",
  2239. Kind: "ReplicationController",
  2240. },
  2241. MinReplicas: &minReplicasVal,
  2242. MaxReplicas: 10,
  2243. Metrics: []autoscaling.MetricSpec{
  2244. {
  2245. Type: autoscaling.ExternalMetricSourceType,
  2246. External: &autoscaling.ExternalMetricSource{
  2247. Metric: autoscaling.MetricIdentifier{
  2248. Name: "some-service-metric",
  2249. Selector: metricLabelSelector,
  2250. },
  2251. Target: autoscaling.MetricTarget{
  2252. Type: autoscaling.ValueMetricType,
  2253. Value: resource.NewMilliQuantity(100, resource.DecimalSI),
  2254. },
  2255. },
  2256. },
  2257. },
  2258. },
  2259. Status: autoscaling.HorizontalPodAutoscalerStatus{
  2260. CurrentReplicas: 4,
  2261. DesiredReplicas: 5,
  2262. },
  2263. },
  2264. "some-hpa ReplicationController/some-rc <unknown>/100m 2 10 4 <unknown>\n",
  2265. },
  2266. // external source type, target value
  2267. {
  2268. autoscaling.HorizontalPodAutoscaler{
  2269. ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  2270. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  2271. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  2272. Name: "some-rc",
  2273. Kind: "ReplicationController",
  2274. },
  2275. MinReplicas: &minReplicasVal,
  2276. MaxReplicas: 10,
  2277. Metrics: []autoscaling.MetricSpec{
  2278. {
  2279. Type: autoscaling.ExternalMetricSourceType,
  2280. External: &autoscaling.ExternalMetricSource{
  2281. Metric: autoscaling.MetricIdentifier{
  2282. Name: "some-external-metric",
  2283. Selector: metricLabelSelector,
  2284. },
  2285. Target: autoscaling.MetricTarget{
  2286. Type: autoscaling.ValueMetricType,
  2287. Value: resource.NewMilliQuantity(100, resource.DecimalSI),
  2288. },
  2289. },
  2290. },
  2291. },
  2292. },
  2293. Status: autoscaling.HorizontalPodAutoscalerStatus{
  2294. CurrentReplicas: 4,
  2295. DesiredReplicas: 5,
  2296. CurrentMetrics: []autoscaling.MetricStatus{
  2297. {
  2298. Type: autoscaling.ExternalMetricSourceType,
  2299. External: &autoscaling.ExternalMetricStatus{
  2300. Metric: autoscaling.MetricIdentifier{
  2301. Name: "some-external-metric",
  2302. },
  2303. Current: autoscaling.MetricValueStatus{
  2304. Value: resource.NewMilliQuantity(50, resource.DecimalSI),
  2305. },
  2306. },
  2307. },
  2308. },
  2309. },
  2310. },
  2311. "some-hpa ReplicationController/some-rc 50m/100m 2 10 4 <unknown>\n",
  2312. },
  2313. // pods source type (no current)
  2314. {
  2315. autoscaling.HorizontalPodAutoscaler{
  2316. ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  2317. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  2318. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  2319. Name: "some-rc",
  2320. Kind: "ReplicationController",
  2321. },
  2322. MinReplicas: &minReplicasVal,
  2323. MaxReplicas: 10,
  2324. Metrics: []autoscaling.MetricSpec{
  2325. {
  2326. Type: autoscaling.PodsMetricSourceType,
  2327. Pods: &autoscaling.PodsMetricSource{
  2328. Metric: autoscaling.MetricIdentifier{
  2329. Name: "some-pods-metric",
  2330. },
  2331. Target: autoscaling.MetricTarget{
  2332. Type: autoscaling.AverageValueMetricType,
  2333. AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  2334. },
  2335. },
  2336. },
  2337. },
  2338. },
  2339. Status: autoscaling.HorizontalPodAutoscalerStatus{
  2340. CurrentReplicas: 4,
  2341. DesiredReplicas: 5,
  2342. },
  2343. },
  2344. "some-hpa ReplicationController/some-rc <unknown>/100m 2 10 4 <unknown>\n",
  2345. },
  2346. // pods source type
  2347. {
  2348. autoscaling.HorizontalPodAutoscaler{
  2349. ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  2350. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  2351. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  2352. Name: "some-rc",
  2353. Kind: "ReplicationController",
  2354. },
  2355. MinReplicas: &minReplicasVal,
  2356. MaxReplicas: 10,
  2357. Metrics: []autoscaling.MetricSpec{
  2358. {
  2359. Type: autoscaling.PodsMetricSourceType,
  2360. Pods: &autoscaling.PodsMetricSource{
  2361. Metric: autoscaling.MetricIdentifier{
  2362. Name: "some-pods-metric",
  2363. },
  2364. Target: autoscaling.MetricTarget{
  2365. Type: autoscaling.AverageValueMetricType,
  2366. AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  2367. },
  2368. },
  2369. },
  2370. },
  2371. },
  2372. Status: autoscaling.HorizontalPodAutoscalerStatus{
  2373. CurrentReplicas: 4,
  2374. DesiredReplicas: 5,
  2375. CurrentMetrics: []autoscaling.MetricStatus{
  2376. {
  2377. Type: autoscaling.PodsMetricSourceType,
  2378. Pods: &autoscaling.PodsMetricStatus{
  2379. Metric: autoscaling.MetricIdentifier{
  2380. Name: "some-pods-metric",
  2381. },
  2382. Current: autoscaling.MetricValueStatus{
  2383. AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
  2384. },
  2385. },
  2386. },
  2387. },
  2388. },
  2389. },
  2390. "some-hpa ReplicationController/some-rc 50m/100m 2 10 4 <unknown>\n",
  2391. },
  2392. // object source type (no current)
  2393. {
  2394. autoscaling.HorizontalPodAutoscaler{
  2395. ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  2396. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  2397. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  2398. Name: "some-rc",
  2399. Kind: "ReplicationController",
  2400. },
  2401. MinReplicas: &minReplicasVal,
  2402. MaxReplicas: 10,
  2403. Metrics: []autoscaling.MetricSpec{
  2404. {
  2405. Type: autoscaling.ObjectMetricSourceType,
  2406. Object: &autoscaling.ObjectMetricSource{
  2407. DescribedObject: autoscaling.CrossVersionObjectReference{
  2408. Name: "some-service",
  2409. Kind: "Service",
  2410. },
  2411. Metric: autoscaling.MetricIdentifier{
  2412. Name: "some-service-metric",
  2413. },
  2414. Target: autoscaling.MetricTarget{
  2415. Type: autoscaling.ValueMetricType,
  2416. Value: resource.NewMilliQuantity(100, resource.DecimalSI),
  2417. },
  2418. },
  2419. },
  2420. },
  2421. },
  2422. Status: autoscaling.HorizontalPodAutoscalerStatus{
  2423. CurrentReplicas: 4,
  2424. DesiredReplicas: 5,
  2425. },
  2426. },
  2427. "some-hpa ReplicationController/some-rc <unknown>/100m 2 10 4 <unknown>\n",
  2428. },
  2429. // object source type
  2430. {
  2431. autoscaling.HorizontalPodAutoscaler{
  2432. ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  2433. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  2434. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  2435. Name: "some-rc",
  2436. Kind: "ReplicationController",
  2437. },
  2438. MinReplicas: &minReplicasVal,
  2439. MaxReplicas: 10,
  2440. Metrics: []autoscaling.MetricSpec{
  2441. {
  2442. Type: autoscaling.ObjectMetricSourceType,
  2443. Object: &autoscaling.ObjectMetricSource{
  2444. DescribedObject: autoscaling.CrossVersionObjectReference{
  2445. Name: "some-service",
  2446. Kind: "Service",
  2447. },
  2448. Metric: autoscaling.MetricIdentifier{
  2449. Name: "some-service-metric",
  2450. },
  2451. Target: autoscaling.MetricTarget{
  2452. Type: autoscaling.ValueMetricType,
  2453. Value: resource.NewMilliQuantity(100, resource.DecimalSI),
  2454. },
  2455. },
  2456. },
  2457. },
  2458. },
  2459. Status: autoscaling.HorizontalPodAutoscalerStatus{
  2460. CurrentReplicas: 4,
  2461. DesiredReplicas: 5,
  2462. CurrentMetrics: []autoscaling.MetricStatus{
  2463. {
  2464. Type: autoscaling.ObjectMetricSourceType,
  2465. Object: &autoscaling.ObjectMetricStatus{
  2466. DescribedObject: autoscaling.CrossVersionObjectReference{
  2467. Name: "some-service",
  2468. Kind: "Service",
  2469. },
  2470. Metric: autoscaling.MetricIdentifier{
  2471. Name: "some-service-metric",
  2472. },
  2473. Current: autoscaling.MetricValueStatus{
  2474. Value: resource.NewMilliQuantity(50, resource.DecimalSI),
  2475. },
  2476. },
  2477. },
  2478. },
  2479. },
  2480. },
  2481. "some-hpa ReplicationController/some-rc 50m/100m 2 10 4 <unknown>\n",
  2482. },
  2483. // resource source type, targetVal (no current)
  2484. {
  2485. autoscaling.HorizontalPodAutoscaler{
  2486. ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  2487. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  2488. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  2489. Name: "some-rc",
  2490. Kind: "ReplicationController",
  2491. },
  2492. MinReplicas: &minReplicasVal,
  2493. MaxReplicas: 10,
  2494. Metrics: []autoscaling.MetricSpec{
  2495. {
  2496. Type: autoscaling.ResourceMetricSourceType,
  2497. Resource: &autoscaling.ResourceMetricSource{
  2498. Name: api.ResourceCPU,
  2499. Target: autoscaling.MetricTarget{
  2500. Type: autoscaling.AverageValueMetricType,
  2501. AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  2502. },
  2503. },
  2504. },
  2505. },
  2506. },
  2507. Status: autoscaling.HorizontalPodAutoscalerStatus{
  2508. CurrentReplicas: 4,
  2509. DesiredReplicas: 5,
  2510. },
  2511. },
  2512. "some-hpa ReplicationController/some-rc <unknown>/100m 2 10 4 <unknown>\n",
  2513. },
  2514. // resource source type, targetVal
  2515. {
  2516. autoscaling.HorizontalPodAutoscaler{
  2517. ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  2518. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  2519. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  2520. Name: "some-rc",
  2521. Kind: "ReplicationController",
  2522. },
  2523. MinReplicas: &minReplicasVal,
  2524. MaxReplicas: 10,
  2525. Metrics: []autoscaling.MetricSpec{
  2526. {
  2527. Type: autoscaling.ResourceMetricSourceType,
  2528. Resource: &autoscaling.ResourceMetricSource{
  2529. Name: api.ResourceCPU,
  2530. Target: autoscaling.MetricTarget{
  2531. Type: autoscaling.AverageValueMetricType,
  2532. AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  2533. },
  2534. },
  2535. },
  2536. },
  2537. },
  2538. Status: autoscaling.HorizontalPodAutoscalerStatus{
  2539. CurrentReplicas: 4,
  2540. DesiredReplicas: 5,
  2541. CurrentMetrics: []autoscaling.MetricStatus{
  2542. {
  2543. Type: autoscaling.ResourceMetricSourceType,
  2544. Resource: &autoscaling.ResourceMetricStatus{
  2545. Name: api.ResourceCPU,
  2546. Current: autoscaling.MetricValueStatus{
  2547. AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
  2548. },
  2549. },
  2550. },
  2551. },
  2552. },
  2553. },
  2554. "some-hpa ReplicationController/some-rc 50m/100m 2 10 4 <unknown>\n",
  2555. },
  2556. // resource source type, targetUtil (no current)
  2557. {
  2558. autoscaling.HorizontalPodAutoscaler{
  2559. ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  2560. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  2561. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  2562. Name: "some-rc",
  2563. Kind: "ReplicationController",
  2564. },
  2565. MinReplicas: &minReplicasVal,
  2566. MaxReplicas: 10,
  2567. Metrics: []autoscaling.MetricSpec{
  2568. {
  2569. Type: autoscaling.ResourceMetricSourceType,
  2570. Resource: &autoscaling.ResourceMetricSource{
  2571. Name: api.ResourceCPU,
  2572. Target: autoscaling.MetricTarget{
  2573. Type: autoscaling.UtilizationMetricType,
  2574. AverageUtilization: &targetUtilizationVal,
  2575. },
  2576. },
  2577. },
  2578. },
  2579. },
  2580. Status: autoscaling.HorizontalPodAutoscalerStatus{
  2581. CurrentReplicas: 4,
  2582. DesiredReplicas: 5,
  2583. },
  2584. },
  2585. "some-hpa ReplicationController/some-rc <unknown>/80% 2 10 4 <unknown>\n",
  2586. },
  2587. // resource source type, targetUtil
  2588. {
  2589. autoscaling.HorizontalPodAutoscaler{
  2590. ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  2591. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  2592. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  2593. Name: "some-rc",
  2594. Kind: "ReplicationController",
  2595. },
  2596. MinReplicas: &minReplicasVal,
  2597. MaxReplicas: 10,
  2598. Metrics: []autoscaling.MetricSpec{
  2599. {
  2600. Type: autoscaling.ResourceMetricSourceType,
  2601. Resource: &autoscaling.ResourceMetricSource{
  2602. Name: api.ResourceCPU,
  2603. Target: autoscaling.MetricTarget{
  2604. Type: autoscaling.UtilizationMetricType,
  2605. AverageUtilization: &targetUtilizationVal,
  2606. },
  2607. },
  2608. },
  2609. },
  2610. },
  2611. Status: autoscaling.HorizontalPodAutoscalerStatus{
  2612. CurrentReplicas: 4,
  2613. DesiredReplicas: 5,
  2614. CurrentMetrics: []autoscaling.MetricStatus{
  2615. {
  2616. Type: autoscaling.ResourceMetricSourceType,
  2617. Resource: &autoscaling.ResourceMetricStatus{
  2618. Name: api.ResourceCPU,
  2619. Current: autoscaling.MetricValueStatus{
  2620. AverageUtilization: &currentUtilizationVal,
  2621. AverageValue: resource.NewMilliQuantity(40, resource.DecimalSI),
  2622. },
  2623. },
  2624. },
  2625. },
  2626. },
  2627. },
  2628. "some-hpa ReplicationController/some-rc 50%/80% 2 10 4 <unknown>\n",
  2629. },
  2630. // multiple specs
  2631. {
  2632. autoscaling.HorizontalPodAutoscaler{
  2633. ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  2634. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  2635. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  2636. Name: "some-rc",
  2637. Kind: "ReplicationController",
  2638. },
  2639. MinReplicas: &minReplicasVal,
  2640. MaxReplicas: 10,
  2641. Metrics: []autoscaling.MetricSpec{
  2642. {
  2643. Type: autoscaling.PodsMetricSourceType,
  2644. Pods: &autoscaling.PodsMetricSource{
  2645. Metric: autoscaling.MetricIdentifier{
  2646. Name: "some-pods-metric",
  2647. },
  2648. Target: autoscaling.MetricTarget{
  2649. Type: autoscaling.AverageValueMetricType,
  2650. AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  2651. },
  2652. },
  2653. },
  2654. {
  2655. Type: autoscaling.ResourceMetricSourceType,
  2656. Resource: &autoscaling.ResourceMetricSource{
  2657. Name: api.ResourceCPU,
  2658. Target: autoscaling.MetricTarget{
  2659. Type: autoscaling.UtilizationMetricType,
  2660. AverageUtilization: &targetUtilizationVal,
  2661. },
  2662. },
  2663. },
  2664. {
  2665. Type: autoscaling.PodsMetricSourceType,
  2666. Pods: &autoscaling.PodsMetricSource{
  2667. Metric: autoscaling.MetricIdentifier{
  2668. Name: "other-pods-metric",
  2669. },
  2670. Target: autoscaling.MetricTarget{
  2671. Type: autoscaling.AverageValueMetricType,
  2672. AverageValue: resource.NewMilliQuantity(400, resource.DecimalSI),
  2673. },
  2674. },
  2675. },
  2676. },
  2677. },
  2678. Status: autoscaling.HorizontalPodAutoscalerStatus{
  2679. CurrentReplicas: 4,
  2680. DesiredReplicas: 5,
  2681. CurrentMetrics: []autoscaling.MetricStatus{
  2682. {
  2683. Type: autoscaling.PodsMetricSourceType,
  2684. Pods: &autoscaling.PodsMetricStatus{
  2685. Metric: autoscaling.MetricIdentifier{
  2686. Name: "some-pods-metric",
  2687. },
  2688. Current: autoscaling.MetricValueStatus{
  2689. AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
  2690. },
  2691. },
  2692. },
  2693. {
  2694. Type: autoscaling.ResourceMetricSourceType,
  2695. Resource: &autoscaling.ResourceMetricStatus{
  2696. Name: api.ResourceCPU,
  2697. Current: autoscaling.MetricValueStatus{
  2698. AverageUtilization: &currentUtilizationVal,
  2699. AverageValue: resource.NewMilliQuantity(40, resource.DecimalSI),
  2700. },
  2701. },
  2702. },
  2703. },
  2704. },
  2705. },
  2706. "some-hpa ReplicationController/some-rc 50m/100m, 50%/80% + 1 more... 2 10 4 <unknown>\n",
  2707. },
  2708. }
  2709. buff := bytes.NewBuffer([]byte{})
  2710. for _, test := range tests {
  2711. table, err := printers.NewTableGenerator().With(AddHandlers).GenerateTable(&test.hpa, printers.PrintOptions{})
  2712. if err != nil {
  2713. t.Fatal(err)
  2714. }
  2715. verifyTable(t, table)
  2716. printer := printers.NewTablePrinter(printers.PrintOptions{NoHeaders: true})
  2717. if err := printer.PrintObj(table, buff); err != nil {
  2718. t.Fatal(err)
  2719. }
  2720. if buff.String() != test.expected {
  2721. t.Errorf("expected %q, got %q", test.expected, buff.String())
  2722. }
  2723. buff.Reset()
  2724. }
  2725. }
  2726. func TestPrintPodShowLabels(t *testing.T) {
  2727. tests := []struct {
  2728. pod api.Pod
  2729. showLabels bool
  2730. expect []metav1beta1.TableRow
  2731. }{
  2732. {
  2733. // Test name, num of containers, restarts, container ready status
  2734. api.Pod{
  2735. ObjectMeta: metav1.ObjectMeta{
  2736. Name: "test1",
  2737. Labels: map[string]string{"col1": "asd", "COL2": "zxc"},
  2738. },
  2739. Spec: api.PodSpec{Containers: make([]api.Container, 2)},
  2740. Status: api.PodStatus{
  2741. Phase: "podPhase",
  2742. ContainerStatuses: []api.ContainerStatus{
  2743. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  2744. {RestartCount: 3},
  2745. },
  2746. },
  2747. },
  2748. true,
  2749. []metav1beta1.TableRow{{Cells: []interface{}{"test1", "1/2", "podPhase", int64(6), "<unknown>", "COL2=zxc,col1=asd"}}},
  2750. },
  2751. {
  2752. // Test name, num of containers, restarts, container ready status
  2753. api.Pod{
  2754. ObjectMeta: metav1.ObjectMeta{
  2755. Name: "test1",
  2756. Labels: map[string]string{"col3": "asd", "COL4": "zxc"},
  2757. },
  2758. Spec: api.PodSpec{Containers: make([]api.Container, 2)},
  2759. Status: api.PodStatus{
  2760. Phase: "podPhase",
  2761. ContainerStatuses: []api.ContainerStatus{
  2762. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  2763. {RestartCount: 3},
  2764. },
  2765. },
  2766. },
  2767. false,
  2768. []metav1beta1.TableRow{{Cells: []interface{}{"test1", "1/2", "podPhase", int64(6), "<unknown>"}}},
  2769. },
  2770. }
  2771. for i, test := range tests {
  2772. table, err := printers.NewTableGenerator().With(AddHandlers).GenerateTable(&test.pod, printers.PrintOptions{ShowLabels: test.showLabels})
  2773. if err != nil {
  2774. t.Fatal(err)
  2775. }
  2776. verifyTable(t, table)
  2777. rows := table.Rows
  2778. for i := range rows {
  2779. rows[i].Object.Object = nil
  2780. }
  2781. if !reflect.DeepEqual(test.expect, rows) {
  2782. t.Errorf("%d mismatch: %s", i, diff.ObjectReflectDiff(test.expect, rows))
  2783. }
  2784. }
  2785. }
  2786. func TestPrintService(t *testing.T) {
  2787. singleExternalIP := []string{"80.11.12.10"}
  2788. mulExternalIP := []string{"80.11.12.10", "80.11.12.11"}
  2789. tests := []struct {
  2790. service api.Service
  2791. expect string
  2792. }{
  2793. {
  2794. // Test name, cluster ip, port with protocol
  2795. api.Service{
  2796. ObjectMeta: metav1.ObjectMeta{Name: "test1"},
  2797. Spec: api.ServiceSpec{
  2798. Type: api.ServiceTypeClusterIP,
  2799. Ports: []api.ServicePort{
  2800. {
  2801. Protocol: "tcp",
  2802. Port: 2233,
  2803. },
  2804. },
  2805. ClusterIP: "10.9.8.7",
  2806. },
  2807. },
  2808. "test1 ClusterIP 10.9.8.7 <none> 2233/tcp <unknown>\n",
  2809. },
  2810. {
  2811. // Test NodePort service
  2812. api.Service{
  2813. ObjectMeta: metav1.ObjectMeta{Name: "test2"},
  2814. Spec: api.ServiceSpec{
  2815. Type: api.ServiceTypeNodePort,
  2816. Ports: []api.ServicePort{
  2817. {
  2818. Protocol: "tcp",
  2819. Port: 8888,
  2820. NodePort: 9999,
  2821. },
  2822. },
  2823. ClusterIP: "10.9.8.7",
  2824. },
  2825. },
  2826. "test2 NodePort 10.9.8.7 <none> 8888:9999/tcp <unknown>\n",
  2827. },
  2828. {
  2829. // Test LoadBalancer service
  2830. api.Service{
  2831. ObjectMeta: metav1.ObjectMeta{Name: "test3"},
  2832. Spec: api.ServiceSpec{
  2833. Type: api.ServiceTypeLoadBalancer,
  2834. Ports: []api.ServicePort{
  2835. {
  2836. Protocol: "tcp",
  2837. Port: 8888,
  2838. },
  2839. },
  2840. ClusterIP: "10.9.8.7",
  2841. },
  2842. },
  2843. "test3 LoadBalancer 10.9.8.7 <pending> 8888/tcp <unknown>\n",
  2844. },
  2845. {
  2846. // Test LoadBalancer service with single ExternalIP and no LoadBalancerStatus
  2847. api.Service{
  2848. ObjectMeta: metav1.ObjectMeta{Name: "test4"},
  2849. Spec: api.ServiceSpec{
  2850. Type: api.ServiceTypeLoadBalancer,
  2851. Ports: []api.ServicePort{
  2852. {
  2853. Protocol: "tcp",
  2854. Port: 8888,
  2855. },
  2856. },
  2857. ClusterIP: "10.9.8.7",
  2858. ExternalIPs: singleExternalIP,
  2859. },
  2860. },
  2861. "test4 LoadBalancer 10.9.8.7 80.11.12.10 8888/tcp <unknown>\n",
  2862. },
  2863. {
  2864. // Test LoadBalancer service with single ExternalIP
  2865. api.Service{
  2866. ObjectMeta: metav1.ObjectMeta{Name: "test5"},
  2867. Spec: api.ServiceSpec{
  2868. Type: api.ServiceTypeLoadBalancer,
  2869. Ports: []api.ServicePort{
  2870. {
  2871. Protocol: "tcp",
  2872. Port: 8888,
  2873. },
  2874. },
  2875. ClusterIP: "10.9.8.7",
  2876. ExternalIPs: singleExternalIP,
  2877. },
  2878. Status: api.ServiceStatus{
  2879. LoadBalancer: api.LoadBalancerStatus{
  2880. Ingress: []api.LoadBalancerIngress{
  2881. {
  2882. IP: "3.4.5.6",
  2883. Hostname: "test.cluster.com",
  2884. },
  2885. },
  2886. },
  2887. },
  2888. },
  2889. "test5 LoadBalancer 10.9.8.7 3.4.5.6,80.11.12.10 8888/tcp <unknown>\n",
  2890. },
  2891. {
  2892. // Test LoadBalancer service with mul ExternalIPs
  2893. api.Service{
  2894. ObjectMeta: metav1.ObjectMeta{Name: "test6"},
  2895. Spec: api.ServiceSpec{
  2896. Type: api.ServiceTypeLoadBalancer,
  2897. Ports: []api.ServicePort{
  2898. {
  2899. Protocol: "tcp",
  2900. Port: 8888,
  2901. },
  2902. },
  2903. ClusterIP: "10.9.8.7",
  2904. ExternalIPs: mulExternalIP,
  2905. },
  2906. Status: api.ServiceStatus{
  2907. LoadBalancer: api.LoadBalancerStatus{
  2908. Ingress: []api.LoadBalancerIngress{
  2909. {
  2910. IP: "2.3.4.5",
  2911. Hostname: "test.cluster.local",
  2912. },
  2913. {
  2914. IP: "3.4.5.6",
  2915. Hostname: "test.cluster.com",
  2916. },
  2917. },
  2918. },
  2919. },
  2920. },
  2921. "test6 LoadBalancer 10.9.8.7 2.3.4.5,3.4.5.6,80.11.12.10,80.11.12.11 8888/tcp <unknown>\n",
  2922. },
  2923. {
  2924. // Test ExternalName service
  2925. api.Service{
  2926. ObjectMeta: metav1.ObjectMeta{Name: "test7"},
  2927. Spec: api.ServiceSpec{
  2928. Type: api.ServiceTypeExternalName,
  2929. ExternalName: "my.database.example.com",
  2930. },
  2931. },
  2932. "test7 ExternalName <none> my.database.example.com <none> <unknown>\n",
  2933. },
  2934. }
  2935. buf := bytes.NewBuffer([]byte{})
  2936. for _, test := range tests {
  2937. table, err := printers.NewTableGenerator().With(AddHandlers).GenerateTable(&test.service, printers.PrintOptions{})
  2938. if err != nil {
  2939. t.Fatal(err)
  2940. }
  2941. verifyTable(t, table)
  2942. printer := printers.NewTablePrinter(printers.PrintOptions{NoHeaders: true})
  2943. if err := printer.PrintObj(table, buf); err != nil {
  2944. t.Fatal(err)
  2945. }
  2946. // We ignore time
  2947. if buf.String() != test.expect {
  2948. t.Errorf("Expected: %s, but got: %s", test.expect, buf.String())
  2949. }
  2950. buf.Reset()
  2951. }
  2952. }
  2953. func TestPrintPodDisruptionBudget(t *testing.T) {
  2954. minAvailable := intstr.FromInt(22)
  2955. maxUnavailable := intstr.FromInt(11)
  2956. tests := []struct {
  2957. pdb policy.PodDisruptionBudget
  2958. expect string
  2959. }{
  2960. {
  2961. policy.PodDisruptionBudget{
  2962. ObjectMeta: metav1.ObjectMeta{
  2963. Namespace: "ns1",
  2964. Name: "pdb1",
  2965. CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  2966. },
  2967. Spec: policy.PodDisruptionBudgetSpec{
  2968. MinAvailable: &minAvailable,
  2969. },
  2970. Status: policy.PodDisruptionBudgetStatus{
  2971. PodDisruptionsAllowed: 5,
  2972. },
  2973. },
  2974. "pdb1 22 N/A 5 0s\n",
  2975. },
  2976. {
  2977. policy.PodDisruptionBudget{
  2978. ObjectMeta: metav1.ObjectMeta{
  2979. Namespace: "ns2",
  2980. Name: "pdb2",
  2981. CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  2982. },
  2983. Spec: policy.PodDisruptionBudgetSpec{
  2984. MaxUnavailable: &maxUnavailable,
  2985. },
  2986. Status: policy.PodDisruptionBudgetStatus{
  2987. PodDisruptionsAllowed: 5,
  2988. },
  2989. },
  2990. "pdb2 N/A 11 5 0s\n",
  2991. }}
  2992. buf := bytes.NewBuffer([]byte{})
  2993. for _, test := range tests {
  2994. table, err := printers.NewTableGenerator().With(AddHandlers).GenerateTable(&test.pdb, printers.PrintOptions{})
  2995. if err != nil {
  2996. t.Fatal(err)
  2997. }
  2998. verifyTable(t, table)
  2999. printer := printers.NewTablePrinter(printers.PrintOptions{NoHeaders: true})
  3000. if err := printer.PrintObj(table, buf); err != nil {
  3001. t.Fatal(err)
  3002. }
  3003. if buf.String() != test.expect {
  3004. t.Errorf("Expected: %s, got: %s", test.expect, buf.String())
  3005. }
  3006. buf.Reset()
  3007. }
  3008. }
  3009. func TestPrintControllerRevision(t *testing.T) {
  3010. tests := []struct {
  3011. history apps.ControllerRevision
  3012. expect string
  3013. }{
  3014. {
  3015. apps.ControllerRevision{
  3016. ObjectMeta: metav1.ObjectMeta{
  3017. Name: "test1",
  3018. CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  3019. OwnerReferences: []metav1.OwnerReference{
  3020. {
  3021. Controller: boolP(true),
  3022. APIVersion: "apps/v1",
  3023. Kind: "DaemonSet",
  3024. Name: "foo",
  3025. },
  3026. },
  3027. },
  3028. Revision: 1,
  3029. },
  3030. "test1 daemonset.apps/foo 1 0s\n",
  3031. },
  3032. {
  3033. apps.ControllerRevision{
  3034. ObjectMeta: metav1.ObjectMeta{
  3035. Name: "test2",
  3036. CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  3037. OwnerReferences: []metav1.OwnerReference{
  3038. {
  3039. Controller: boolP(false),
  3040. Kind: "ABC",
  3041. Name: "foo",
  3042. },
  3043. },
  3044. },
  3045. Revision: 2,
  3046. },
  3047. "test2 <none> 2 0s\n",
  3048. },
  3049. {
  3050. apps.ControllerRevision{
  3051. ObjectMeta: metav1.ObjectMeta{
  3052. Name: "test3",
  3053. CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  3054. OwnerReferences: []metav1.OwnerReference{},
  3055. },
  3056. Revision: 3,
  3057. },
  3058. "test3 <none> 3 0s\n",
  3059. },
  3060. {
  3061. apps.ControllerRevision{
  3062. ObjectMeta: metav1.ObjectMeta{
  3063. Name: "test4",
  3064. CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  3065. OwnerReferences: nil,
  3066. },
  3067. Revision: 4,
  3068. },
  3069. "test4 <none> 4 0s\n",
  3070. },
  3071. }
  3072. buf := bytes.NewBuffer([]byte{})
  3073. for _, test := range tests {
  3074. table, err := printers.NewTableGenerator().With(AddHandlers).GenerateTable(&test.history, printers.PrintOptions{})
  3075. if err != nil {
  3076. t.Fatal(err)
  3077. }
  3078. verifyTable(t, table)
  3079. printer := printers.NewTablePrinter(printers.PrintOptions{NoHeaders: true})
  3080. if err := printer.PrintObj(table, buf); err != nil {
  3081. t.Fatal(err)
  3082. }
  3083. if buf.String() != test.expect {
  3084. t.Errorf("Expected: %s, but got: %s", test.expect, buf.String())
  3085. }
  3086. buf.Reset()
  3087. }
  3088. }
  3089. func boolP(b bool) *bool {
  3090. return &b
  3091. }
  3092. func TestPrintReplicaSet(t *testing.T) {
  3093. tests := []struct {
  3094. replicaSet apps.ReplicaSet
  3095. expect string
  3096. wideExpect string
  3097. }{
  3098. {
  3099. apps.ReplicaSet{
  3100. ObjectMeta: metav1.ObjectMeta{
  3101. Name: "test1",
  3102. CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  3103. },
  3104. Spec: apps.ReplicaSetSpec{
  3105. Replicas: 5,
  3106. Template: api.PodTemplateSpec{
  3107. Spec: api.PodSpec{
  3108. Containers: []api.Container{
  3109. {
  3110. Name: "fake-container1",
  3111. Image: "fake-image1",
  3112. },
  3113. {
  3114. Name: "fake-container2",
  3115. Image: "fake-image2",
  3116. },
  3117. },
  3118. },
  3119. },
  3120. Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  3121. },
  3122. Status: apps.ReplicaSetStatus{
  3123. Replicas: 5,
  3124. ReadyReplicas: 2,
  3125. },
  3126. },
  3127. "test1 5 5 2 0s\n",
  3128. "test1 5 5 2 0s fake-container1,fake-container2 fake-image1,fake-image2 foo=bar\n",
  3129. },
  3130. }
  3131. buf := bytes.NewBuffer([]byte{})
  3132. for _, test := range tests {
  3133. table, err := printers.NewTableGenerator().With(AddHandlers).GenerateTable(&test.replicaSet, printers.PrintOptions{})
  3134. if err != nil {
  3135. t.Fatal(err)
  3136. }
  3137. verifyTable(t, table)
  3138. printer := printers.NewTablePrinter(printers.PrintOptions{NoHeaders: true})
  3139. if err := printer.PrintObj(table, buf); err != nil {
  3140. t.Fatal(err)
  3141. }
  3142. if buf.String() != test.expect {
  3143. t.Errorf("Expected: %s, got: %s", test.expect, buf.String())
  3144. }
  3145. buf.Reset()
  3146. table, err = printers.NewTableGenerator().With(AddHandlers).GenerateTable(&test.replicaSet, printers.PrintOptions{Wide: true})
  3147. if err != nil {
  3148. t.Fatal(err)
  3149. }
  3150. verifyTable(t, table)
  3151. printer = printers.NewTablePrinter(printers.PrintOptions{NoHeaders: true, Wide: true})
  3152. if err := printer.PrintObj(table, buf); err != nil {
  3153. t.Fatal(err)
  3154. }
  3155. if buf.String() != test.wideExpect {
  3156. t.Errorf("Expected: %s, got: %s", test.wideExpect, buf.String())
  3157. }
  3158. buf.Reset()
  3159. }
  3160. }
  3161. func TestPrintPersistentVolume(t *testing.T) {
  3162. myScn := "my-scn"
  3163. claimRef := api.ObjectReference{
  3164. Name: "test",
  3165. Namespace: "default",
  3166. }
  3167. tests := []struct {
  3168. pv api.PersistentVolume
  3169. expect string
  3170. }{
  3171. {
  3172. // Test bound
  3173. api.PersistentVolume{
  3174. ObjectMeta: metav1.ObjectMeta{
  3175. Name: "test1",
  3176. },
  3177. Spec: api.PersistentVolumeSpec{
  3178. ClaimRef: &claimRef,
  3179. AccessModes: []api.PersistentVolumeAccessMode{api.ReadOnlyMany},
  3180. Capacity: map[api.ResourceName]resource.Quantity{
  3181. api.ResourceStorage: resource.MustParse("4Gi"),
  3182. },
  3183. },
  3184. Status: api.PersistentVolumeStatus{
  3185. Phase: api.VolumeBound,
  3186. },
  3187. },
  3188. "test1 4Gi ROX Bound default/test <unknown>\n",
  3189. },
  3190. {
  3191. // // Test failed
  3192. api.PersistentVolume{
  3193. ObjectMeta: metav1.ObjectMeta{
  3194. Name: "test2",
  3195. },
  3196. Spec: api.PersistentVolumeSpec{
  3197. ClaimRef: &claimRef,
  3198. AccessModes: []api.PersistentVolumeAccessMode{api.ReadOnlyMany},
  3199. Capacity: map[api.ResourceName]resource.Quantity{
  3200. api.ResourceStorage: resource.MustParse("4Gi"),
  3201. },
  3202. },
  3203. Status: api.PersistentVolumeStatus{
  3204. Phase: api.VolumeFailed,
  3205. },
  3206. },
  3207. "test2 4Gi ROX Failed default/test <unknown>\n",
  3208. },
  3209. {
  3210. // Test pending
  3211. api.PersistentVolume{
  3212. ObjectMeta: metav1.ObjectMeta{
  3213. Name: "test3",
  3214. },
  3215. Spec: api.PersistentVolumeSpec{
  3216. ClaimRef: &claimRef,
  3217. AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteMany},
  3218. Capacity: map[api.ResourceName]resource.Quantity{
  3219. api.ResourceStorage: resource.MustParse("10Gi"),
  3220. },
  3221. },
  3222. Status: api.PersistentVolumeStatus{
  3223. Phase: api.VolumePending,
  3224. },
  3225. },
  3226. "test3 10Gi RWX Pending default/test <unknown>\n",
  3227. },
  3228. {
  3229. // Test pending, storageClass
  3230. api.PersistentVolume{
  3231. ObjectMeta: metav1.ObjectMeta{
  3232. Name: "test4",
  3233. },
  3234. Spec: api.PersistentVolumeSpec{
  3235. ClaimRef: &claimRef,
  3236. StorageClassName: myScn,
  3237. AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
  3238. Capacity: map[api.ResourceName]resource.Quantity{
  3239. api.ResourceStorage: resource.MustParse("10Gi"),
  3240. },
  3241. },
  3242. Status: api.PersistentVolumeStatus{
  3243. Phase: api.VolumePending,
  3244. },
  3245. },
  3246. "test4 10Gi RWO Pending default/test my-scn <unknown>\n",
  3247. },
  3248. {
  3249. // Test available
  3250. api.PersistentVolume{
  3251. ObjectMeta: metav1.ObjectMeta{
  3252. Name: "test5",
  3253. },
  3254. Spec: api.PersistentVolumeSpec{
  3255. ClaimRef: &claimRef,
  3256. StorageClassName: myScn,
  3257. AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
  3258. Capacity: map[api.ResourceName]resource.Quantity{
  3259. api.ResourceStorage: resource.MustParse("10Gi"),
  3260. },
  3261. },
  3262. Status: api.PersistentVolumeStatus{
  3263. Phase: api.VolumeAvailable,
  3264. },
  3265. },
  3266. "test5 10Gi RWO Available default/test my-scn <unknown>\n",
  3267. },
  3268. {
  3269. // Test released
  3270. api.PersistentVolume{
  3271. ObjectMeta: metav1.ObjectMeta{
  3272. Name: "test6",
  3273. },
  3274. Spec: api.PersistentVolumeSpec{
  3275. ClaimRef: &claimRef,
  3276. StorageClassName: myScn,
  3277. AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
  3278. Capacity: map[api.ResourceName]resource.Quantity{
  3279. api.ResourceStorage: resource.MustParse("10Gi"),
  3280. },
  3281. },
  3282. Status: api.PersistentVolumeStatus{
  3283. Phase: api.VolumeReleased,
  3284. },
  3285. },
  3286. "test6 10Gi RWO Released default/test my-scn <unknown>\n",
  3287. },
  3288. }
  3289. buf := bytes.NewBuffer([]byte{})
  3290. for _, test := range tests {
  3291. table, err := printers.NewTableGenerator().With(AddHandlers).GenerateTable(&test.pv, printers.PrintOptions{})
  3292. if err != nil {
  3293. t.Fatal(err)
  3294. }
  3295. verifyTable(t, table)
  3296. printer := printers.NewTablePrinter(printers.PrintOptions{NoHeaders: true})
  3297. if err := printer.PrintObj(table, buf); err != nil {
  3298. t.Fatal(err)
  3299. }
  3300. if buf.String() != test.expect {
  3301. t.Errorf("Expected: %s, but got: %s", test.expect, buf.String())
  3302. }
  3303. buf.Reset()
  3304. }
  3305. }
  3306. func TestPrintPersistentVolumeClaim(t *testing.T) {
  3307. volumeMode := api.PersistentVolumeFilesystem
  3308. myScn := "my-scn"
  3309. tests := []struct {
  3310. pvc api.PersistentVolumeClaim
  3311. expect string
  3312. }{
  3313. {
  3314. // Test name, num of containers, restarts, container ready status
  3315. api.PersistentVolumeClaim{
  3316. ObjectMeta: metav1.ObjectMeta{
  3317. Name: "test1",
  3318. },
  3319. Spec: api.PersistentVolumeClaimSpec{
  3320. VolumeName: "my-volume",
  3321. VolumeMode: &volumeMode,
  3322. },
  3323. Status: api.PersistentVolumeClaimStatus{
  3324. Phase: api.ClaimBound,
  3325. AccessModes: []api.PersistentVolumeAccessMode{api.ReadOnlyMany},
  3326. Capacity: map[api.ResourceName]resource.Quantity{
  3327. api.ResourceStorage: resource.MustParse("4Gi"),
  3328. },
  3329. },
  3330. },
  3331. "test1 Bound my-volume 4Gi ROX <unknown> Filesystem\n",
  3332. },
  3333. {
  3334. // Test name, num of containers, restarts, container ready status
  3335. api.PersistentVolumeClaim{
  3336. ObjectMeta: metav1.ObjectMeta{
  3337. Name: "test2",
  3338. },
  3339. Spec: api.PersistentVolumeClaimSpec{
  3340. VolumeMode: &volumeMode,
  3341. },
  3342. Status: api.PersistentVolumeClaimStatus{
  3343. Phase: api.ClaimLost,
  3344. AccessModes: []api.PersistentVolumeAccessMode{api.ReadOnlyMany},
  3345. Capacity: map[api.ResourceName]resource.Quantity{
  3346. api.ResourceStorage: resource.MustParse("4Gi"),
  3347. },
  3348. },
  3349. },
  3350. "test2 Lost <unknown> Filesystem\n",
  3351. },
  3352. {
  3353. // Test name, num of containers, restarts, container ready status
  3354. api.PersistentVolumeClaim{
  3355. ObjectMeta: metav1.ObjectMeta{
  3356. Name: "test3",
  3357. },
  3358. Spec: api.PersistentVolumeClaimSpec{
  3359. VolumeName: "my-volume",
  3360. VolumeMode: &volumeMode,
  3361. },
  3362. Status: api.PersistentVolumeClaimStatus{
  3363. Phase: api.ClaimPending,
  3364. AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteMany},
  3365. Capacity: map[api.ResourceName]resource.Quantity{
  3366. api.ResourceStorage: resource.MustParse("10Gi"),
  3367. },
  3368. },
  3369. },
  3370. "test3 Pending my-volume 10Gi RWX <unknown> Filesystem\n",
  3371. },
  3372. {
  3373. // Test name, num of containers, restarts, container ready status
  3374. api.PersistentVolumeClaim{
  3375. ObjectMeta: metav1.ObjectMeta{
  3376. Name: "test4",
  3377. },
  3378. Spec: api.PersistentVolumeClaimSpec{
  3379. VolumeName: "my-volume",
  3380. StorageClassName: &myScn,
  3381. VolumeMode: &volumeMode,
  3382. },
  3383. Status: api.PersistentVolumeClaimStatus{
  3384. Phase: api.ClaimPending,
  3385. AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
  3386. Capacity: map[api.ResourceName]resource.Quantity{
  3387. api.ResourceStorage: resource.MustParse("10Gi"),
  3388. },
  3389. },
  3390. },
  3391. "test4 Pending my-volume 10Gi RWO my-scn <unknown> Filesystem\n",
  3392. },
  3393. {
  3394. // Test name, num of containers, restarts, container ready status
  3395. api.PersistentVolumeClaim{
  3396. ObjectMeta: metav1.ObjectMeta{
  3397. Name: "test5",
  3398. },
  3399. Spec: api.PersistentVolumeClaimSpec{
  3400. VolumeName: "my-volume",
  3401. StorageClassName: &myScn,
  3402. },
  3403. Status: api.PersistentVolumeClaimStatus{
  3404. Phase: api.ClaimPending,
  3405. AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
  3406. Capacity: map[api.ResourceName]resource.Quantity{
  3407. api.ResourceStorage: resource.MustParse("10Gi"),
  3408. },
  3409. },
  3410. },
  3411. "test5 Pending my-volume 10Gi RWO my-scn <unknown> <unset>\n",
  3412. },
  3413. }
  3414. buf := bytes.NewBuffer([]byte{})
  3415. for _, test := range tests {
  3416. table, err := printers.NewTableGenerator().With(AddHandlers).GenerateTable(&test.pvc, printers.PrintOptions{Wide: true})
  3417. if err != nil {
  3418. t.Fatal(err)
  3419. }
  3420. verifyTable(t, table)
  3421. printer := printers.NewTablePrinter(printers.PrintOptions{NoHeaders: true, Wide: true})
  3422. if err := printer.PrintObj(table, buf); err != nil {
  3423. t.Fatal(err)
  3424. }
  3425. if buf.String() != test.expect {
  3426. t.Errorf("Expected: %s, but got: %s", test.expect, buf.String())
  3427. }
  3428. buf.Reset()
  3429. }
  3430. }
  3431. func TestPrintCronJob(t *testing.T) {
  3432. suspend := false
  3433. tests := []struct {
  3434. cronjob batch.CronJob
  3435. expect string
  3436. }{
  3437. {
  3438. batch.CronJob{
  3439. ObjectMeta: metav1.ObjectMeta{
  3440. Name: "cronjob1",
  3441. CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  3442. },
  3443. Spec: batch.CronJobSpec{
  3444. Schedule: "0/5 * * * ?",
  3445. Suspend: &suspend,
  3446. },
  3447. Status: batch.CronJobStatus{
  3448. LastScheduleTime: &metav1.Time{Time: time.Now().Add(1.9e9)},
  3449. },
  3450. },
  3451. "cronjob1 0/5 * * * ? False 0 0s 0s\n",
  3452. },
  3453. {
  3454. batch.CronJob{
  3455. ObjectMeta: metav1.ObjectMeta{
  3456. Name: "cronjob2",
  3457. CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  3458. },
  3459. Spec: batch.CronJobSpec{
  3460. Schedule: "0/5 * * * ?",
  3461. Suspend: &suspend,
  3462. },
  3463. Status: batch.CronJobStatus{
  3464. LastScheduleTime: &metav1.Time{Time: time.Now().Add(-3e10)},
  3465. },
  3466. },
  3467. "cronjob2 0/5 * * * ? False 0 30s 5m\n",
  3468. },
  3469. {
  3470. batch.CronJob{
  3471. ObjectMeta: metav1.ObjectMeta{
  3472. Name: "cronjob3",
  3473. CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  3474. },
  3475. Spec: batch.CronJobSpec{
  3476. Schedule: "0/5 * * * ?",
  3477. Suspend: &suspend,
  3478. },
  3479. Status: batch.CronJobStatus{},
  3480. },
  3481. "cronjob3 0/5 * * * ? False 0 <none> 5m\n",
  3482. },
  3483. }
  3484. buf := bytes.NewBuffer([]byte{})
  3485. for _, test := range tests {
  3486. table, err := printers.NewTableGenerator().With(AddHandlers).GenerateTable(&test.cronjob, printers.PrintOptions{})
  3487. if err != nil {
  3488. t.Fatal(err)
  3489. }
  3490. verifyTable(t, table)
  3491. printer := printers.NewTablePrinter(printers.PrintOptions{NoHeaders: true})
  3492. if err := printer.PrintObj(table, buf); err != nil {
  3493. t.Fatal(err)
  3494. }
  3495. if buf.String() != test.expect {
  3496. t.Errorf("Expected: %s, got: %s", test.expect, buf.String())
  3497. }
  3498. buf.Reset()
  3499. }
  3500. }
  3501. func TestPrintStorageClass(t *testing.T) {
  3502. tests := []struct {
  3503. sc storage.StorageClass
  3504. expect string
  3505. }{
  3506. {
  3507. storage.StorageClass{
  3508. ObjectMeta: metav1.ObjectMeta{
  3509. Name: "sc1",
  3510. CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  3511. },
  3512. Provisioner: "kubernetes.io/glusterfs",
  3513. },
  3514. "sc1 kubernetes.io/glusterfs 0s\n",
  3515. },
  3516. {
  3517. storage.StorageClass{
  3518. ObjectMeta: metav1.ObjectMeta{
  3519. Name: "sc2",
  3520. CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  3521. },
  3522. Provisioner: "kubernetes.io/nfs",
  3523. },
  3524. "sc2 kubernetes.io/nfs 5m\n",
  3525. },
  3526. }
  3527. buf := bytes.NewBuffer([]byte{})
  3528. for _, test := range tests {
  3529. table, err := printers.NewTableGenerator().With(AddHandlers).GenerateTable(&test.sc, printers.PrintOptions{})
  3530. if err != nil {
  3531. t.Fatal(err)
  3532. }
  3533. verifyTable(t, table)
  3534. printer := printers.NewTablePrinter(printers.PrintOptions{NoHeaders: true})
  3535. if err := printer.PrintObj(table, buf); err != nil {
  3536. t.Fatal(err)
  3537. }
  3538. if buf.String() != test.expect {
  3539. t.Errorf("Expected: %s, got: %s", test.expect, buf.String())
  3540. }
  3541. buf.Reset()
  3542. }
  3543. }
  3544. func TestPrintLease(t *testing.T) {
  3545. holder1 := "holder1"
  3546. holder2 := "holder2"
  3547. tests := []struct {
  3548. sc coordination.Lease
  3549. expect string
  3550. }{
  3551. {
  3552. coordination.Lease{
  3553. ObjectMeta: metav1.ObjectMeta{
  3554. Name: "lease1",
  3555. CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  3556. },
  3557. Spec: coordination.LeaseSpec{
  3558. HolderIdentity: &holder1,
  3559. },
  3560. },
  3561. "lease1 holder1 0s\n",
  3562. },
  3563. {
  3564. coordination.Lease{
  3565. ObjectMeta: metav1.ObjectMeta{
  3566. Name: "lease2",
  3567. CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  3568. },
  3569. Spec: coordination.LeaseSpec{
  3570. HolderIdentity: &holder2,
  3571. },
  3572. },
  3573. "lease2 holder2 5m\n",
  3574. },
  3575. }
  3576. buf := bytes.NewBuffer([]byte{})
  3577. for _, test := range tests {
  3578. table, err := printers.NewTableGenerator().With(AddHandlers).GenerateTable(&test.sc, printers.PrintOptions{})
  3579. if err != nil {
  3580. t.Fatal(err)
  3581. }
  3582. printer := printers.NewTablePrinter(printers.PrintOptions{NoHeaders: true})
  3583. if err := printer.PrintObj(table, buf); err != nil {
  3584. t.Fatal(err)
  3585. }
  3586. if buf.String() != test.expect {
  3587. t.Errorf("Expected: %s, got: %s", test.expect, buf.String())
  3588. }
  3589. buf.Reset()
  3590. }
  3591. }
  3592. func TestPrintPriorityClass(t *testing.T) {
  3593. tests := []struct {
  3594. pc scheduling.PriorityClass
  3595. expect string
  3596. }{
  3597. {
  3598. scheduling.PriorityClass{
  3599. ObjectMeta: metav1.ObjectMeta{
  3600. Name: "pc1",
  3601. CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  3602. },
  3603. Value: 1,
  3604. },
  3605. "pc1 1 false 0s\n",
  3606. },
  3607. {
  3608. scheduling.PriorityClass{
  3609. ObjectMeta: metav1.ObjectMeta{
  3610. Name: "pc2",
  3611. CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  3612. },
  3613. Value: 1000000000,
  3614. GlobalDefault: true,
  3615. },
  3616. "pc2 1000000000 true 5m\n",
  3617. },
  3618. }
  3619. buf := bytes.NewBuffer([]byte{})
  3620. for _, test := range tests {
  3621. table, err := printers.NewTableGenerator().With(AddHandlers).GenerateTable(&test.pc, printers.PrintOptions{})
  3622. if err != nil {
  3623. t.Fatal(err)
  3624. }
  3625. verifyTable(t, table)
  3626. printer := printers.NewTablePrinter(printers.PrintOptions{NoHeaders: true})
  3627. if err := printer.PrintObj(table, buf); err != nil {
  3628. t.Fatal(err)
  3629. }
  3630. if buf.String() != test.expect {
  3631. t.Errorf("Expected: %s, got: %s", test.expect, buf.String())
  3632. }
  3633. buf.Reset()
  3634. }
  3635. }
  3636. func TestPrintRuntimeClass(t *testing.T) {
  3637. tests := []struct {
  3638. rc nodeapi.RuntimeClass
  3639. expect string
  3640. }{
  3641. {
  3642. nodeapi.RuntimeClass{
  3643. ObjectMeta: metav1.ObjectMeta{
  3644. Name: "rc1",
  3645. CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  3646. },
  3647. Handler: "h1",
  3648. },
  3649. "rc1 h1 0s\n",
  3650. },
  3651. {
  3652. nodeapi.RuntimeClass{
  3653. ObjectMeta: metav1.ObjectMeta{
  3654. Name: "rc2",
  3655. CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  3656. },
  3657. Handler: "h2",
  3658. },
  3659. "rc2 h2 5m\n",
  3660. },
  3661. }
  3662. buf := bytes.NewBuffer([]byte{})
  3663. for _, test := range tests {
  3664. table, err := printers.NewTableGenerator().With(AddHandlers).GenerateTable(&test.rc, printers.PrintOptions{})
  3665. if err != nil {
  3666. t.Fatal(err)
  3667. }
  3668. verifyTable(t, table)
  3669. printer := printers.NewTablePrinter(printers.PrintOptions{NoHeaders: true})
  3670. if err := printer.PrintObj(table, buf); err != nil {
  3671. t.Fatal(err)
  3672. }
  3673. if buf.String() != test.expect {
  3674. t.Errorf("Expected: %s, got: %s", test.expect, buf.String())
  3675. }
  3676. buf.Reset()
  3677. }
  3678. }
  3679. func verifyTable(t *testing.T, table *metav1beta1.Table) {
  3680. var panicErr interface{}
  3681. func() {
  3682. defer func() {
  3683. panicErr = recover()
  3684. }()
  3685. table.DeepCopyObject() // cells are untyped, better check that types are JSON types and can be deep copied
  3686. }()
  3687. if panicErr != nil {
  3688. t.Errorf("unexpected panic during deepcopy of table %#v: %v", table, panicErr)
  3689. }
  3690. }
  3691. // VerifyDatesInOrder checks the start of each line for a RFC1123Z date
  3692. // and posts error if all subsequent dates are not equal or increasing
  3693. func VerifyDatesInOrder(
  3694. resultToTest, rowDelimiter, columnDelimiter string, t *testing.T) {
  3695. lines := strings.Split(resultToTest, rowDelimiter)
  3696. var previousTime time.Time
  3697. for _, str := range lines {
  3698. columns := strings.Split(str, columnDelimiter)
  3699. if len(columns) > 0 {
  3700. currentTime, err := time.Parse(time.RFC1123Z, columns[0])
  3701. if err == nil {
  3702. if previousTime.After(currentTime) {
  3703. t.Errorf(
  3704. "Output is not sorted by time. %s should be listed after %s. Complete output: %s",
  3705. previousTime.Format(time.RFC1123Z),
  3706. currentTime.Format(time.RFC1123Z),
  3707. resultToTest)
  3708. }
  3709. previousTime = currentTime
  3710. }
  3711. }
  3712. }
  3713. }