server_test.go 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691
  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 server
  14. import (
  15. "bytes"
  16. "context"
  17. "encoding/json"
  18. "errors"
  19. "fmt"
  20. "io"
  21. "io/ioutil"
  22. "net"
  23. "net/http"
  24. "net/http/httptest"
  25. "net/http/httputil"
  26. "net/url"
  27. "reflect"
  28. "strconv"
  29. "strings"
  30. "testing"
  31. "time"
  32. cadvisorapi "github.com/google/cadvisor/info/v1"
  33. "github.com/stretchr/testify/assert"
  34. "github.com/stretchr/testify/require"
  35. "k8s.io/api/core/v1"
  36. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  37. "k8s.io/apimachinery/pkg/types"
  38. "k8s.io/apimachinery/pkg/util/httpstream"
  39. "k8s.io/apimachinery/pkg/util/httpstream/spdy"
  40. "k8s.io/apimachinery/pkg/util/sets"
  41. "k8s.io/apiserver/pkg/authentication/authenticator"
  42. "k8s.io/apiserver/pkg/authentication/user"
  43. "k8s.io/apiserver/pkg/authorization/authorizer"
  44. "k8s.io/client-go/tools/remotecommand"
  45. utiltesting "k8s.io/client-go/util/testing"
  46. runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
  47. api "k8s.io/kubernetes/pkg/apis/core"
  48. statsapi "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1"
  49. // Do some initialization to decode the query parameters correctly.
  50. _ "k8s.io/kubernetes/pkg/apis/core/install"
  51. "k8s.io/kubernetes/pkg/kubelet/cm"
  52. kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
  53. "k8s.io/kubernetes/pkg/kubelet/server/portforward"
  54. remotecommandserver "k8s.io/kubernetes/pkg/kubelet/server/remotecommand"
  55. "k8s.io/kubernetes/pkg/kubelet/server/stats"
  56. "k8s.io/kubernetes/pkg/kubelet/server/streaming"
  57. "k8s.io/kubernetes/pkg/volume"
  58. )
  59. const (
  60. testUID = "9b01b80f-8fb4-11e4-95ab-4200af06647"
  61. testContainerID = "container789"
  62. testPodSandboxID = "pod0987"
  63. )
  64. type fakeKubelet struct {
  65. podByNameFunc func(namespace, name string) (*v1.Pod, bool)
  66. containerInfoFunc func(podFullName string, uid types.UID, containerName string, req *cadvisorapi.ContainerInfoRequest) (*cadvisorapi.ContainerInfo, error)
  67. rawInfoFunc func(query *cadvisorapi.ContainerInfoRequest) (map[string]*cadvisorapi.ContainerInfo, error)
  68. machineInfoFunc func() (*cadvisorapi.MachineInfo, error)
  69. podsFunc func() []*v1.Pod
  70. runningPodsFunc func() ([]*v1.Pod, error)
  71. logFunc func(w http.ResponseWriter, req *http.Request)
  72. runFunc func(podFullName string, uid types.UID, containerName string, cmd []string) ([]byte, error)
  73. getExecCheck func(string, types.UID, string, []string, remotecommandserver.Options)
  74. getAttachCheck func(string, types.UID, string, remotecommandserver.Options)
  75. getPortForwardCheck func(string, string, types.UID, portforward.V4Options)
  76. containerLogsFunc func(ctx context.Context, podFullName, containerName string, logOptions *v1.PodLogOptions, stdout, stderr io.Writer) error
  77. hostnameFunc func() string
  78. resyncInterval time.Duration
  79. loopEntryTime time.Time
  80. plegHealth bool
  81. streamingRuntime streaming.Server
  82. }
  83. func (fk *fakeKubelet) ResyncInterval() time.Duration {
  84. return fk.resyncInterval
  85. }
  86. func (fk *fakeKubelet) LatestLoopEntryTime() time.Time {
  87. return fk.loopEntryTime
  88. }
  89. func (fk *fakeKubelet) GetPodByName(namespace, name string) (*v1.Pod, bool) {
  90. return fk.podByNameFunc(namespace, name)
  91. }
  92. func (fk *fakeKubelet) GetContainerInfo(podFullName string, uid types.UID, containerName string, req *cadvisorapi.ContainerInfoRequest) (*cadvisorapi.ContainerInfo, error) {
  93. return fk.containerInfoFunc(podFullName, uid, containerName, req)
  94. }
  95. func (fk *fakeKubelet) GetRawContainerInfo(containerName string, req *cadvisorapi.ContainerInfoRequest, subcontainers bool) (map[string]*cadvisorapi.ContainerInfo, error) {
  96. return fk.rawInfoFunc(req)
  97. }
  98. func (fk *fakeKubelet) GetCachedMachineInfo() (*cadvisorapi.MachineInfo, error) {
  99. return fk.machineInfoFunc()
  100. }
  101. func (*fakeKubelet) GetVersionInfo() (*cadvisorapi.VersionInfo, error) {
  102. return &cadvisorapi.VersionInfo{}, nil
  103. }
  104. func (fk *fakeKubelet) GetPods() []*v1.Pod {
  105. return fk.podsFunc()
  106. }
  107. func (fk *fakeKubelet) GetRunningPods() ([]*v1.Pod, error) {
  108. return fk.runningPodsFunc()
  109. }
  110. func (fk *fakeKubelet) ServeLogs(w http.ResponseWriter, req *http.Request) {
  111. fk.logFunc(w, req)
  112. }
  113. func (fk *fakeKubelet) GetKubeletContainerLogs(ctx context.Context, podFullName, containerName string, logOptions *v1.PodLogOptions, stdout, stderr io.Writer) error {
  114. return fk.containerLogsFunc(ctx, podFullName, containerName, logOptions, stdout, stderr)
  115. }
  116. func (fk *fakeKubelet) GetHostname() string {
  117. return fk.hostnameFunc()
  118. }
  119. func (fk *fakeKubelet) RunInContainer(podFullName string, uid types.UID, containerName string, cmd []string) ([]byte, error) {
  120. return fk.runFunc(podFullName, uid, containerName, cmd)
  121. }
  122. type fakeRuntime struct {
  123. execFunc func(string, []string, io.Reader, io.WriteCloser, io.WriteCloser, bool, <-chan remotecommand.TerminalSize) error
  124. attachFunc func(string, io.Reader, io.WriteCloser, io.WriteCloser, bool, <-chan remotecommand.TerminalSize) error
  125. portForwardFunc func(string, int32, io.ReadWriteCloser) error
  126. }
  127. func (f *fakeRuntime) Exec(containerID string, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize) error {
  128. return f.execFunc(containerID, cmd, stdin, stdout, stderr, tty, resize)
  129. }
  130. func (f *fakeRuntime) Attach(containerID string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize) error {
  131. return f.attachFunc(containerID, stdin, stdout, stderr, tty, resize)
  132. }
  133. func (f *fakeRuntime) PortForward(podSandboxID string, port int32, stream io.ReadWriteCloser) error {
  134. return f.portForwardFunc(podSandboxID, port, stream)
  135. }
  136. type testStreamingServer struct {
  137. streaming.Server
  138. fakeRuntime *fakeRuntime
  139. testHTTPServer *httptest.Server
  140. }
  141. func newTestStreamingServer(streamIdleTimeout time.Duration) (s *testStreamingServer, err error) {
  142. s = &testStreamingServer{}
  143. s.testHTTPServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  144. s.ServeHTTP(w, r)
  145. }))
  146. defer func() {
  147. if err != nil {
  148. s.testHTTPServer.Close()
  149. }
  150. }()
  151. testURL, err := url.Parse(s.testHTTPServer.URL)
  152. if err != nil {
  153. return nil, err
  154. }
  155. s.fakeRuntime = &fakeRuntime{}
  156. config := streaming.DefaultConfig
  157. config.BaseURL = testURL
  158. if streamIdleTimeout != 0 {
  159. config.StreamIdleTimeout = streamIdleTimeout
  160. }
  161. s.Server, err = streaming.NewServer(config, s.fakeRuntime)
  162. if err != nil {
  163. return nil, err
  164. }
  165. return s, nil
  166. }
  167. func (fk *fakeKubelet) GetExec(podFullName string, podUID types.UID, containerName string, cmd []string, streamOpts remotecommandserver.Options) (*url.URL, error) {
  168. if fk.getExecCheck != nil {
  169. fk.getExecCheck(podFullName, podUID, containerName, cmd, streamOpts)
  170. }
  171. // Always use testContainerID
  172. resp, err := fk.streamingRuntime.GetExec(&runtimeapi.ExecRequest{
  173. ContainerId: testContainerID,
  174. Cmd: cmd,
  175. Tty: streamOpts.TTY,
  176. Stdin: streamOpts.Stdin,
  177. Stdout: streamOpts.Stdout,
  178. Stderr: streamOpts.Stderr,
  179. })
  180. if err != nil {
  181. return nil, err
  182. }
  183. return url.Parse(resp.GetUrl())
  184. }
  185. func (fk *fakeKubelet) GetAttach(podFullName string, podUID types.UID, containerName string, streamOpts remotecommandserver.Options) (*url.URL, error) {
  186. if fk.getAttachCheck != nil {
  187. fk.getAttachCheck(podFullName, podUID, containerName, streamOpts)
  188. }
  189. // Always use testContainerID
  190. resp, err := fk.streamingRuntime.GetAttach(&runtimeapi.AttachRequest{
  191. ContainerId: testContainerID,
  192. Tty: streamOpts.TTY,
  193. Stdin: streamOpts.Stdin,
  194. Stdout: streamOpts.Stdout,
  195. Stderr: streamOpts.Stderr,
  196. })
  197. if err != nil {
  198. return nil, err
  199. }
  200. return url.Parse(resp.GetUrl())
  201. }
  202. func (fk *fakeKubelet) GetPortForward(podName, podNamespace string, podUID types.UID, portForwardOpts portforward.V4Options) (*url.URL, error) {
  203. if fk.getPortForwardCheck != nil {
  204. fk.getPortForwardCheck(podName, podNamespace, podUID, portForwardOpts)
  205. }
  206. // Always use testPodSandboxID
  207. resp, err := fk.streamingRuntime.GetPortForward(&runtimeapi.PortForwardRequest{
  208. PodSandboxId: testPodSandboxID,
  209. Port: portForwardOpts.Ports,
  210. })
  211. if err != nil {
  212. return nil, err
  213. }
  214. return url.Parse(resp.GetUrl())
  215. }
  216. // Unused functions
  217. func (*fakeKubelet) GetNode() (*v1.Node, error) { return nil, nil }
  218. func (*fakeKubelet) GetNodeConfig() cm.NodeConfig { return cm.NodeConfig{} }
  219. func (*fakeKubelet) GetPodCgroupRoot() string { return "" }
  220. func (*fakeKubelet) GetPodByCgroupfs(cgroupfs string) (*v1.Pod, bool) { return nil, false }
  221. func (fk *fakeKubelet) ListVolumesForPod(podUID types.UID) (map[string]volume.Volume, bool) {
  222. return map[string]volume.Volume{}, true
  223. }
  224. func (*fakeKubelet) RootFsStats() (*statsapi.FsStats, error) { return nil, nil }
  225. func (*fakeKubelet) ListPodStats() ([]statsapi.PodStats, error) { return nil, nil }
  226. func (*fakeKubelet) ListPodStatsAndUpdateCPUNanoCoreUsage() ([]statsapi.PodStats, error) {
  227. return nil, nil
  228. }
  229. func (*fakeKubelet) ListPodCPUAndMemoryStats() ([]statsapi.PodStats, error) { return nil, nil }
  230. func (*fakeKubelet) ImageFsStats() (*statsapi.FsStats, error) { return nil, nil }
  231. func (*fakeKubelet) RlimitStats() (*statsapi.RlimitStats, error) { return nil, nil }
  232. func (*fakeKubelet) GetCgroupStats(cgroupName string, updateStats bool) (*statsapi.ContainerStats, *statsapi.NetworkStats, error) {
  233. return nil, nil, nil
  234. }
  235. func (*fakeKubelet) GetCgroupCPUAndMemoryStats(cgroupName string, updateStats bool) (*statsapi.ContainerStats, error) {
  236. return nil, nil
  237. }
  238. type fakeAuth struct {
  239. authenticateFunc func(*http.Request) (*authenticator.Response, bool, error)
  240. attributesFunc func(user.Info, *http.Request) authorizer.Attributes
  241. authorizeFunc func(authorizer.Attributes) (authorized authorizer.Decision, reason string, err error)
  242. }
  243. func (f *fakeAuth) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {
  244. return f.authenticateFunc(req)
  245. }
  246. func (f *fakeAuth) GetRequestAttributes(u user.Info, req *http.Request) authorizer.Attributes {
  247. return f.attributesFunc(u, req)
  248. }
  249. func (f *fakeAuth) Authorize(a authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) {
  250. return f.authorizeFunc(a)
  251. }
  252. type serverTestFramework struct {
  253. serverUnderTest *Server
  254. fakeKubelet *fakeKubelet
  255. fakeAuth *fakeAuth
  256. testHTTPServer *httptest.Server
  257. fakeRuntime *fakeRuntime
  258. testStreamingHTTPServer *httptest.Server
  259. criHandler *utiltesting.FakeHandler
  260. }
  261. func newServerTest() *serverTestFramework {
  262. return newServerTestWithDebug(true, false, nil)
  263. }
  264. func newServerTestWithDebug(enableDebugging, redirectContainerStreaming bool, streamingServer streaming.Server) *serverTestFramework {
  265. fw := &serverTestFramework{}
  266. fw.fakeKubelet = &fakeKubelet{
  267. hostnameFunc: func() string {
  268. return "127.0.0.1"
  269. },
  270. podByNameFunc: func(namespace, name string) (*v1.Pod, bool) {
  271. return &v1.Pod{
  272. ObjectMeta: metav1.ObjectMeta{
  273. Namespace: namespace,
  274. Name: name,
  275. UID: testUID,
  276. },
  277. }, true
  278. },
  279. plegHealth: true,
  280. streamingRuntime: streamingServer,
  281. }
  282. fw.fakeAuth = &fakeAuth{
  283. authenticateFunc: func(req *http.Request) (*authenticator.Response, bool, error) {
  284. return &authenticator.Response{User: &user.DefaultInfo{Name: "test"}}, true, nil
  285. },
  286. attributesFunc: func(u user.Info, req *http.Request) authorizer.Attributes {
  287. return &authorizer.AttributesRecord{User: u}
  288. },
  289. authorizeFunc: func(a authorizer.Attributes) (decision authorizer.Decision, reason string, err error) {
  290. return authorizer.DecisionAllow, "", nil
  291. },
  292. }
  293. fw.criHandler = &utiltesting.FakeHandler{
  294. StatusCode: http.StatusOK,
  295. }
  296. server := NewServer(
  297. fw.fakeKubelet,
  298. stats.NewResourceAnalyzer(fw.fakeKubelet, time.Minute),
  299. fw.fakeAuth,
  300. true,
  301. enableDebugging,
  302. false,
  303. redirectContainerStreaming,
  304. fw.criHandler)
  305. fw.serverUnderTest = &server
  306. fw.testHTTPServer = httptest.NewServer(fw.serverUnderTest)
  307. return fw
  308. }
  309. // A helper function to return the correct pod name.
  310. func getPodName(name, namespace string) string {
  311. if namespace == "" {
  312. namespace = metav1.NamespaceDefault
  313. }
  314. return name + "_" + namespace
  315. }
  316. func TestContainerInfo(t *testing.T) {
  317. fw := newServerTest()
  318. defer fw.testHTTPServer.Close()
  319. expectedInfo := &cadvisorapi.ContainerInfo{}
  320. podID := "somepod"
  321. expectedPodID := getPodName(podID, "")
  322. expectedContainerName := "goodcontainer"
  323. fw.fakeKubelet.containerInfoFunc = func(podID string, uid types.UID, containerName string, req *cadvisorapi.ContainerInfoRequest) (*cadvisorapi.ContainerInfo, error) {
  324. if podID != expectedPodID || containerName != expectedContainerName {
  325. return nil, fmt.Errorf("bad podID or containerName: podID=%v; containerName=%v", podID, containerName)
  326. }
  327. return expectedInfo, nil
  328. }
  329. resp, err := http.Get(fw.testHTTPServer.URL + fmt.Sprintf("/stats/%v/%v", podID, expectedContainerName))
  330. if err != nil {
  331. t.Fatalf("Got error GETing: %v", err)
  332. }
  333. defer resp.Body.Close()
  334. var receivedInfo cadvisorapi.ContainerInfo
  335. err = json.NewDecoder(resp.Body).Decode(&receivedInfo)
  336. if err != nil {
  337. t.Fatalf("received invalid json data: %v", err)
  338. }
  339. if !receivedInfo.Eq(expectedInfo) {
  340. t.Errorf("received wrong data: %#v", receivedInfo)
  341. }
  342. }
  343. func TestContainerInfoWithUidNamespace(t *testing.T) {
  344. fw := newServerTest()
  345. defer fw.testHTTPServer.Close()
  346. expectedInfo := &cadvisorapi.ContainerInfo{}
  347. podID := "somepod"
  348. expectedNamespace := "custom"
  349. expectedPodID := getPodName(podID, expectedNamespace)
  350. expectedContainerName := "goodcontainer"
  351. fw.fakeKubelet.containerInfoFunc = func(podID string, uid types.UID, containerName string, req *cadvisorapi.ContainerInfoRequest) (*cadvisorapi.ContainerInfo, error) {
  352. if podID != expectedPodID || string(uid) != testUID || containerName != expectedContainerName {
  353. return nil, fmt.Errorf("bad podID or uid or containerName: podID=%v; uid=%v; containerName=%v", podID, uid, containerName)
  354. }
  355. return expectedInfo, nil
  356. }
  357. resp, err := http.Get(fw.testHTTPServer.URL + fmt.Sprintf("/stats/%v/%v/%v/%v", expectedNamespace, podID, testUID, expectedContainerName))
  358. if err != nil {
  359. t.Fatalf("Got error GETing: %v", err)
  360. }
  361. defer resp.Body.Close()
  362. var receivedInfo cadvisorapi.ContainerInfo
  363. err = json.NewDecoder(resp.Body).Decode(&receivedInfo)
  364. if err != nil {
  365. t.Fatalf("received invalid json data: %v", err)
  366. }
  367. if !receivedInfo.Eq(expectedInfo) {
  368. t.Errorf("received wrong data: %#v", receivedInfo)
  369. }
  370. }
  371. func TestContainerNotFound(t *testing.T) {
  372. fw := newServerTest()
  373. defer fw.testHTTPServer.Close()
  374. podID := "somepod"
  375. expectedNamespace := "custom"
  376. expectedContainerName := "slowstartcontainer"
  377. fw.fakeKubelet.containerInfoFunc = func(podID string, uid types.UID, containerName string, req *cadvisorapi.ContainerInfoRequest) (*cadvisorapi.ContainerInfo, error) {
  378. return nil, kubecontainer.ErrContainerNotFound
  379. }
  380. resp, err := http.Get(fw.testHTTPServer.URL + fmt.Sprintf("/stats/%v/%v/%v/%v", expectedNamespace, podID, testUID, expectedContainerName))
  381. if err != nil {
  382. t.Fatalf("Got error GETing: %v", err)
  383. }
  384. if resp.StatusCode != http.StatusNotFound {
  385. t.Fatalf("Received status %d expecting %d", resp.StatusCode, http.StatusNotFound)
  386. }
  387. defer resp.Body.Close()
  388. }
  389. func TestRootInfo(t *testing.T) {
  390. fw := newServerTest()
  391. defer fw.testHTTPServer.Close()
  392. expectedInfo := &cadvisorapi.ContainerInfo{
  393. ContainerReference: cadvisorapi.ContainerReference{
  394. Name: "/",
  395. },
  396. }
  397. fw.fakeKubelet.rawInfoFunc = func(req *cadvisorapi.ContainerInfoRequest) (map[string]*cadvisorapi.ContainerInfo, error) {
  398. return map[string]*cadvisorapi.ContainerInfo{
  399. expectedInfo.Name: expectedInfo,
  400. }, nil
  401. }
  402. resp, err := http.Get(fw.testHTTPServer.URL + "/stats")
  403. if err != nil {
  404. t.Fatalf("Got error GETing: %v", err)
  405. }
  406. defer resp.Body.Close()
  407. var receivedInfo cadvisorapi.ContainerInfo
  408. err = json.NewDecoder(resp.Body).Decode(&receivedInfo)
  409. if err != nil {
  410. t.Fatalf("received invalid json data: %v", err)
  411. }
  412. if !receivedInfo.Eq(expectedInfo) {
  413. t.Errorf("received wrong data: %#v, expected %#v", receivedInfo, expectedInfo)
  414. }
  415. }
  416. func TestSubcontainerContainerInfo(t *testing.T) {
  417. fw := newServerTest()
  418. defer fw.testHTTPServer.Close()
  419. const kubeletContainer = "/kubelet"
  420. const kubeletSubContainer = "/kubelet/sub"
  421. expectedInfo := map[string]*cadvisorapi.ContainerInfo{
  422. kubeletContainer: {
  423. ContainerReference: cadvisorapi.ContainerReference{
  424. Name: kubeletContainer,
  425. },
  426. },
  427. kubeletSubContainer: {
  428. ContainerReference: cadvisorapi.ContainerReference{
  429. Name: kubeletSubContainer,
  430. },
  431. },
  432. }
  433. fw.fakeKubelet.rawInfoFunc = func(req *cadvisorapi.ContainerInfoRequest) (map[string]*cadvisorapi.ContainerInfo, error) {
  434. return expectedInfo, nil
  435. }
  436. request := fmt.Sprintf("{\"containerName\":%q, \"subcontainers\": true}", kubeletContainer)
  437. resp, err := http.Post(fw.testHTTPServer.URL+"/stats/container", "application/json", bytes.NewBuffer([]byte(request)))
  438. if err != nil {
  439. t.Fatalf("Got error GETing: %v", err)
  440. }
  441. defer resp.Body.Close()
  442. var receivedInfo map[string]*cadvisorapi.ContainerInfo
  443. err = json.NewDecoder(resp.Body).Decode(&receivedInfo)
  444. if err != nil {
  445. t.Fatalf("Received invalid json data: %v", err)
  446. }
  447. if len(receivedInfo) != len(expectedInfo) {
  448. t.Errorf("Received wrong data: %#v, expected %#v", receivedInfo, expectedInfo)
  449. }
  450. for _, containerName := range []string{kubeletContainer, kubeletSubContainer} {
  451. if _, ok := receivedInfo[containerName]; !ok {
  452. t.Errorf("Expected container %q to be present in result: %#v", containerName, receivedInfo)
  453. }
  454. if !receivedInfo[containerName].Eq(expectedInfo[containerName]) {
  455. t.Errorf("Invalid result for %q: Expected %#v, received %#v", containerName, expectedInfo[containerName], receivedInfo[containerName])
  456. }
  457. }
  458. }
  459. func TestMachineInfo(t *testing.T) {
  460. fw := newServerTest()
  461. defer fw.testHTTPServer.Close()
  462. expectedInfo := &cadvisorapi.MachineInfo{
  463. NumCores: 4,
  464. MemoryCapacity: 1024,
  465. }
  466. fw.fakeKubelet.machineInfoFunc = func() (*cadvisorapi.MachineInfo, error) {
  467. return expectedInfo, nil
  468. }
  469. resp, err := http.Get(fw.testHTTPServer.URL + "/spec")
  470. if err != nil {
  471. t.Fatalf("Got error GETing: %v", err)
  472. }
  473. defer resp.Body.Close()
  474. var receivedInfo cadvisorapi.MachineInfo
  475. err = json.NewDecoder(resp.Body).Decode(&receivedInfo)
  476. if err != nil {
  477. t.Fatalf("received invalid json data: %v", err)
  478. }
  479. if !reflect.DeepEqual(&receivedInfo, expectedInfo) {
  480. t.Errorf("received wrong data: %#v", receivedInfo)
  481. }
  482. }
  483. func TestServeLogs(t *testing.T) {
  484. fw := newServerTest()
  485. defer fw.testHTTPServer.Close()
  486. content := string(`<pre><a href="kubelet.log">kubelet.log</a><a href="google.log">google.log</a></pre>`)
  487. fw.fakeKubelet.logFunc = func(w http.ResponseWriter, req *http.Request) {
  488. w.WriteHeader(http.StatusOK)
  489. w.Header().Add("Content-Type", "text/html")
  490. w.Write([]byte(content))
  491. }
  492. resp, err := http.Get(fw.testHTTPServer.URL + "/logs/")
  493. if err != nil {
  494. t.Fatalf("Got error GETing: %v", err)
  495. }
  496. defer resp.Body.Close()
  497. body, err := httputil.DumpResponse(resp, true)
  498. if err != nil {
  499. // copying the response body did not work
  500. t.Errorf("Cannot copy resp: %#v", err)
  501. }
  502. result := string(body)
  503. if !strings.Contains(result, "kubelet.log") || !strings.Contains(result, "google.log") {
  504. t.Errorf("Received wrong data: %s", result)
  505. }
  506. }
  507. func TestServeRunInContainer(t *testing.T) {
  508. fw := newServerTest()
  509. defer fw.testHTTPServer.Close()
  510. output := "foo bar"
  511. podNamespace := "other"
  512. podName := "foo"
  513. expectedPodName := getPodName(podName, podNamespace)
  514. expectedContainerName := "baz"
  515. expectedCommand := "ls -a"
  516. fw.fakeKubelet.runFunc = func(podFullName string, uid types.UID, containerName string, cmd []string) ([]byte, error) {
  517. if podFullName != expectedPodName {
  518. t.Errorf("expected %s, got %s", expectedPodName, podFullName)
  519. }
  520. if containerName != expectedContainerName {
  521. t.Errorf("expected %s, got %s", expectedContainerName, containerName)
  522. }
  523. if strings.Join(cmd, " ") != expectedCommand {
  524. t.Errorf("expected: %s, got %v", expectedCommand, cmd)
  525. }
  526. return []byte(output), nil
  527. }
  528. resp, err := http.Post(fw.testHTTPServer.URL+"/run/"+podNamespace+"/"+podName+"/"+expectedContainerName+"?cmd=ls%20-a", "", nil)
  529. if err != nil {
  530. t.Fatalf("Got error POSTing: %v", err)
  531. }
  532. defer resp.Body.Close()
  533. body, err := ioutil.ReadAll(resp.Body)
  534. if err != nil {
  535. // copying the response body did not work
  536. t.Errorf("Cannot copy resp: %#v", err)
  537. }
  538. result := string(body)
  539. if result != output {
  540. t.Errorf("expected %s, got %s", output, result)
  541. }
  542. }
  543. func TestServeRunInContainerWithUID(t *testing.T) {
  544. fw := newServerTest()
  545. defer fw.testHTTPServer.Close()
  546. output := "foo bar"
  547. podNamespace := "other"
  548. podName := "foo"
  549. expectedPodName := getPodName(podName, podNamespace)
  550. expectedContainerName := "baz"
  551. expectedCommand := "ls -a"
  552. fw.fakeKubelet.runFunc = func(podFullName string, uid types.UID, containerName string, cmd []string) ([]byte, error) {
  553. if podFullName != expectedPodName {
  554. t.Errorf("expected %s, got %s", expectedPodName, podFullName)
  555. }
  556. if string(uid) != testUID {
  557. t.Errorf("expected %s, got %s", testUID, uid)
  558. }
  559. if containerName != expectedContainerName {
  560. t.Errorf("expected %s, got %s", expectedContainerName, containerName)
  561. }
  562. if strings.Join(cmd, " ") != expectedCommand {
  563. t.Errorf("expected: %s, got %v", expectedCommand, cmd)
  564. }
  565. return []byte(output), nil
  566. }
  567. resp, err := http.Post(fw.testHTTPServer.URL+"/run/"+podNamespace+"/"+podName+"/"+testUID+"/"+expectedContainerName+"?cmd=ls%20-a", "", nil)
  568. if err != nil {
  569. t.Fatalf("Got error POSTing: %v", err)
  570. }
  571. defer resp.Body.Close()
  572. body, err := ioutil.ReadAll(resp.Body)
  573. if err != nil {
  574. // copying the response body did not work
  575. t.Errorf("Cannot copy resp: %#v", err)
  576. }
  577. result := string(body)
  578. if result != output {
  579. t.Errorf("expected %s, got %s", output, result)
  580. }
  581. }
  582. func TestHealthCheck(t *testing.T) {
  583. fw := newServerTest()
  584. defer fw.testHTTPServer.Close()
  585. fw.fakeKubelet.hostnameFunc = func() string {
  586. return "127.0.0.1"
  587. }
  588. // Test with correct hostname, Docker version
  589. assertHealthIsOk(t, fw.testHTTPServer.URL+"/healthz")
  590. // Test with incorrect hostname
  591. fw.fakeKubelet.hostnameFunc = func() string {
  592. return "fake"
  593. }
  594. assertHealthIsOk(t, fw.testHTTPServer.URL+"/healthz")
  595. }
  596. func assertHealthFails(t *testing.T, httpURL string, expectedErrorCode int) {
  597. resp, err := http.Get(httpURL)
  598. if err != nil {
  599. t.Fatalf("Got error GETing: %v", err)
  600. }
  601. defer resp.Body.Close()
  602. if resp.StatusCode != expectedErrorCode {
  603. t.Errorf("expected status code %d, got %d", expectedErrorCode, resp.StatusCode)
  604. }
  605. }
  606. type authTestCase struct {
  607. Method string
  608. Path string
  609. }
  610. func TestAuthFilters(t *testing.T) {
  611. fw := newServerTest()
  612. defer fw.testHTTPServer.Close()
  613. testcases := []authTestCase{}
  614. // This is a sanity check that the Handle->HandleWithFilter() delegation is working
  615. // Ideally, these would move to registered web services and this list would get shorter
  616. expectedPaths := []string{"/healthz", "/metrics", "/metrics/cadvisor"}
  617. paths := sets.NewString(fw.serverUnderTest.restfulCont.RegisteredHandlePaths()...)
  618. for _, expectedPath := range expectedPaths {
  619. if !paths.Has(expectedPath) {
  620. t.Errorf("Expected registered handle path %s was missing", expectedPath)
  621. }
  622. }
  623. // Test all the non-web-service handlers
  624. for _, path := range fw.serverUnderTest.restfulCont.RegisteredHandlePaths() {
  625. testcases = append(testcases, authTestCase{"GET", path})
  626. testcases = append(testcases, authTestCase{"POST", path})
  627. // Test subpaths for directory handlers
  628. if strings.HasSuffix(path, "/") {
  629. testcases = append(testcases, authTestCase{"GET", path + "foo"})
  630. testcases = append(testcases, authTestCase{"POST", path + "foo"})
  631. }
  632. }
  633. // Test all the generated web-service paths
  634. for _, ws := range fw.serverUnderTest.restfulCont.RegisteredWebServices() {
  635. for _, r := range ws.Routes() {
  636. testcases = append(testcases, authTestCase{r.Method, r.Path})
  637. }
  638. }
  639. methodToAPIVerb := map[string]string{"GET": "get", "POST": "create", "PUT": "update"}
  640. pathToSubresource := func(path string) string {
  641. switch {
  642. // Cases for subpaths we expect specific subresources for
  643. case isSubpath(path, statsPath):
  644. return "stats"
  645. case isSubpath(path, specPath):
  646. return "spec"
  647. case isSubpath(path, logsPath):
  648. return "log"
  649. case isSubpath(path, metricsPath):
  650. return "metrics"
  651. // Cases for subpaths we expect to map to the "proxy" subresource
  652. case isSubpath(path, "/attach"),
  653. isSubpath(path, "/configz"),
  654. isSubpath(path, "/containerLogs"),
  655. isSubpath(path, "/debug"),
  656. isSubpath(path, "/exec"),
  657. isSubpath(path, "/healthz"),
  658. isSubpath(path, "/pods"),
  659. isSubpath(path, "/portForward"),
  660. isSubpath(path, "/run"),
  661. isSubpath(path, "/runningpods"),
  662. isSubpath(path, "/cri"):
  663. return "proxy"
  664. default:
  665. panic(fmt.Errorf(`unexpected kubelet API path %s.
  666. The kubelet API has likely registered a handler for a new path.
  667. If the new path has a use case for partitioned authorization when requested from the kubelet API,
  668. add a specific subresource for it in auth.go#GetRequestAttributes() and in TestAuthFilters().
  669. Otherwise, add it to the expected list of paths that map to the "proxy" subresource in TestAuthFilters()`, path))
  670. }
  671. }
  672. attributesGetter := NewNodeAuthorizerAttributesGetter(types.NodeName("test"))
  673. for _, tc := range testcases {
  674. var (
  675. expectedUser = &user.DefaultInfo{Name: "test"}
  676. expectedAttributes = authorizer.AttributesRecord{
  677. User: expectedUser,
  678. APIGroup: "",
  679. APIVersion: "v1",
  680. Verb: methodToAPIVerb[tc.Method],
  681. Resource: "nodes",
  682. Name: "test",
  683. Subresource: pathToSubresource(tc.Path),
  684. ResourceRequest: true,
  685. Path: tc.Path,
  686. }
  687. calledAuthenticate = false
  688. calledAuthorize = false
  689. calledAttributes = false
  690. )
  691. fw.fakeAuth.authenticateFunc = func(req *http.Request) (*authenticator.Response, bool, error) {
  692. calledAuthenticate = true
  693. return &authenticator.Response{User: expectedUser}, true, nil
  694. }
  695. fw.fakeAuth.attributesFunc = func(u user.Info, req *http.Request) authorizer.Attributes {
  696. calledAttributes = true
  697. if u != expectedUser {
  698. t.Fatalf("%s: expected user %v, got %v", tc.Path, expectedUser, u)
  699. }
  700. return attributesGetter.GetRequestAttributes(u, req)
  701. }
  702. fw.fakeAuth.authorizeFunc = func(a authorizer.Attributes) (decision authorizer.Decision, reason string, err error) {
  703. calledAuthorize = true
  704. if a != expectedAttributes {
  705. t.Fatalf("%s: expected attributes\n\t%#v\ngot\n\t%#v", tc.Path, expectedAttributes, a)
  706. }
  707. return authorizer.DecisionNoOpinion, "", nil
  708. }
  709. req, err := http.NewRequest(tc.Method, fw.testHTTPServer.URL+tc.Path, nil)
  710. if err != nil {
  711. t.Errorf("%s: unexpected error: %v", tc.Path, err)
  712. continue
  713. }
  714. resp, err := http.DefaultClient.Do(req)
  715. if err != nil {
  716. t.Errorf("%s: unexpected error: %v", tc.Path, err)
  717. continue
  718. }
  719. defer resp.Body.Close()
  720. if resp.StatusCode != http.StatusForbidden {
  721. t.Errorf("%s: unexpected status code %d", tc.Path, resp.StatusCode)
  722. continue
  723. }
  724. if !calledAuthenticate {
  725. t.Errorf("%s: Authenticate was not called", tc.Path)
  726. continue
  727. }
  728. if !calledAttributes {
  729. t.Errorf("%s: Attributes were not called", tc.Path)
  730. continue
  731. }
  732. if !calledAuthorize {
  733. t.Errorf("%s: Authorize was not called", tc.Path)
  734. continue
  735. }
  736. }
  737. }
  738. func TestAuthenticationError(t *testing.T) {
  739. var (
  740. expectedUser = &user.DefaultInfo{Name: "test"}
  741. expectedAttributes = &authorizer.AttributesRecord{User: expectedUser}
  742. calledAuthenticate = false
  743. calledAuthorize = false
  744. calledAttributes = false
  745. )
  746. fw := newServerTest()
  747. defer fw.testHTTPServer.Close()
  748. fw.fakeAuth.authenticateFunc = func(req *http.Request) (*authenticator.Response, bool, error) {
  749. calledAuthenticate = true
  750. return &authenticator.Response{User: expectedUser}, true, nil
  751. }
  752. fw.fakeAuth.attributesFunc = func(u user.Info, req *http.Request) authorizer.Attributes {
  753. calledAttributes = true
  754. return expectedAttributes
  755. }
  756. fw.fakeAuth.authorizeFunc = func(a authorizer.Attributes) (decision authorizer.Decision, reason string, err error) {
  757. calledAuthorize = true
  758. return authorizer.DecisionNoOpinion, "", errors.New("Failed")
  759. }
  760. assertHealthFails(t, fw.testHTTPServer.URL+"/healthz", http.StatusInternalServerError)
  761. if !calledAuthenticate {
  762. t.Fatalf("Authenticate was not called")
  763. }
  764. if !calledAttributes {
  765. t.Fatalf("Attributes was not called")
  766. }
  767. if !calledAuthorize {
  768. t.Fatalf("Authorize was not called")
  769. }
  770. }
  771. func TestAuthenticationFailure(t *testing.T) {
  772. var (
  773. expectedUser = &user.DefaultInfo{Name: "test"}
  774. expectedAttributes = &authorizer.AttributesRecord{User: expectedUser}
  775. calledAuthenticate = false
  776. calledAuthorize = false
  777. calledAttributes = false
  778. )
  779. fw := newServerTest()
  780. defer fw.testHTTPServer.Close()
  781. fw.fakeAuth.authenticateFunc = func(req *http.Request) (*authenticator.Response, bool, error) {
  782. calledAuthenticate = true
  783. return nil, false, nil
  784. }
  785. fw.fakeAuth.attributesFunc = func(u user.Info, req *http.Request) authorizer.Attributes {
  786. calledAttributes = true
  787. return expectedAttributes
  788. }
  789. fw.fakeAuth.authorizeFunc = func(a authorizer.Attributes) (decision authorizer.Decision, reason string, err error) {
  790. calledAuthorize = true
  791. return authorizer.DecisionNoOpinion, "", nil
  792. }
  793. assertHealthFails(t, fw.testHTTPServer.URL+"/healthz", http.StatusUnauthorized)
  794. if !calledAuthenticate {
  795. t.Fatalf("Authenticate was not called")
  796. }
  797. if calledAttributes {
  798. t.Fatalf("Attributes was called unexpectedly")
  799. }
  800. if calledAuthorize {
  801. t.Fatalf("Authorize was called unexpectedly")
  802. }
  803. }
  804. func TestAuthorizationSuccess(t *testing.T) {
  805. var (
  806. expectedUser = &user.DefaultInfo{Name: "test"}
  807. expectedAttributes = &authorizer.AttributesRecord{User: expectedUser}
  808. calledAuthenticate = false
  809. calledAuthorize = false
  810. calledAttributes = false
  811. )
  812. fw := newServerTest()
  813. defer fw.testHTTPServer.Close()
  814. fw.fakeAuth.authenticateFunc = func(req *http.Request) (*authenticator.Response, bool, error) {
  815. calledAuthenticate = true
  816. return &authenticator.Response{User: expectedUser}, true, nil
  817. }
  818. fw.fakeAuth.attributesFunc = func(u user.Info, req *http.Request) authorizer.Attributes {
  819. calledAttributes = true
  820. return expectedAttributes
  821. }
  822. fw.fakeAuth.authorizeFunc = func(a authorizer.Attributes) (decision authorizer.Decision, reason string, err error) {
  823. calledAuthorize = true
  824. return authorizer.DecisionAllow, "", nil
  825. }
  826. assertHealthIsOk(t, fw.testHTTPServer.URL+"/healthz")
  827. if !calledAuthenticate {
  828. t.Fatalf("Authenticate was not called")
  829. }
  830. if !calledAttributes {
  831. t.Fatalf("Attributes were not called")
  832. }
  833. if !calledAuthorize {
  834. t.Fatalf("Authorize was not called")
  835. }
  836. }
  837. func TestSyncLoopCheck(t *testing.T) {
  838. fw := newServerTest()
  839. defer fw.testHTTPServer.Close()
  840. fw.fakeKubelet.hostnameFunc = func() string {
  841. return "127.0.0.1"
  842. }
  843. fw.fakeKubelet.resyncInterval = time.Minute
  844. fw.fakeKubelet.loopEntryTime = time.Now()
  845. // Test with correct hostname, Docker version
  846. assertHealthIsOk(t, fw.testHTTPServer.URL+"/healthz")
  847. fw.fakeKubelet.loopEntryTime = time.Now().Add(time.Minute * -10)
  848. assertHealthFails(t, fw.testHTTPServer.URL+"/healthz", http.StatusInternalServerError)
  849. }
  850. // returns http response status code from the HTTP GET
  851. func assertHealthIsOk(t *testing.T, httpURL string) {
  852. resp, err := http.Get(httpURL)
  853. if err != nil {
  854. t.Fatalf("Got error GETing: %v", err)
  855. }
  856. defer resp.Body.Close()
  857. if resp.StatusCode != http.StatusOK {
  858. t.Errorf("expected status code %d, got %d", http.StatusOK, resp.StatusCode)
  859. }
  860. body, readErr := ioutil.ReadAll(resp.Body)
  861. if readErr != nil {
  862. // copying the response body did not work
  863. t.Fatalf("Cannot copy resp: %#v", readErr)
  864. }
  865. result := string(body)
  866. if !strings.Contains(result, "ok") {
  867. t.Errorf("expected body contains ok, got %s", result)
  868. }
  869. }
  870. func setPodByNameFunc(fw *serverTestFramework, namespace, pod, container string) {
  871. fw.fakeKubelet.podByNameFunc = func(namespace, name string) (*v1.Pod, bool) {
  872. return &v1.Pod{
  873. ObjectMeta: metav1.ObjectMeta{
  874. Namespace: namespace,
  875. Name: pod,
  876. },
  877. Spec: v1.PodSpec{
  878. Containers: []v1.Container{
  879. {
  880. Name: container,
  881. },
  882. },
  883. },
  884. }, true
  885. }
  886. }
  887. func setGetContainerLogsFunc(fw *serverTestFramework, t *testing.T, expectedPodName, expectedContainerName string, expectedLogOptions *v1.PodLogOptions, output string) {
  888. fw.fakeKubelet.containerLogsFunc = func(_ context.Context, podFullName, containerName string, logOptions *v1.PodLogOptions, stdout, stderr io.Writer) error {
  889. if podFullName != expectedPodName {
  890. t.Errorf("expected %s, got %s", expectedPodName, podFullName)
  891. }
  892. if containerName != expectedContainerName {
  893. t.Errorf("expected %s, got %s", expectedContainerName, containerName)
  894. }
  895. if !reflect.DeepEqual(expectedLogOptions, logOptions) {
  896. t.Errorf("expected %#v, got %#v", expectedLogOptions, logOptions)
  897. }
  898. io.WriteString(stdout, output)
  899. return nil
  900. }
  901. }
  902. // TODO: I really want to be a table driven test
  903. func TestContainerLogs(t *testing.T) {
  904. fw := newServerTest()
  905. defer fw.testHTTPServer.Close()
  906. output := "foo bar"
  907. podNamespace := "other"
  908. podName := "foo"
  909. expectedPodName := getPodName(podName, podNamespace)
  910. expectedContainerName := "baz"
  911. setPodByNameFunc(fw, podNamespace, podName, expectedContainerName)
  912. setGetContainerLogsFunc(fw, t, expectedPodName, expectedContainerName, &v1.PodLogOptions{}, output)
  913. resp, err := http.Get(fw.testHTTPServer.URL + "/containerLogs/" + podNamespace + "/" + podName + "/" + expectedContainerName)
  914. if err != nil {
  915. t.Errorf("Got error GETing: %v", err)
  916. }
  917. defer resp.Body.Close()
  918. body, err := ioutil.ReadAll(resp.Body)
  919. if err != nil {
  920. t.Errorf("Error reading container logs: %v", err)
  921. }
  922. result := string(body)
  923. if result != output {
  924. t.Errorf("Expected: '%v', got: '%v'", output, result)
  925. }
  926. }
  927. func TestContainerLogsWithTail(t *testing.T) {
  928. fw := newServerTest()
  929. defer fw.testHTTPServer.Close()
  930. output := "foo bar"
  931. podNamespace := "other"
  932. podName := "foo"
  933. expectedPodName := getPodName(podName, podNamespace)
  934. expectedContainerName := "baz"
  935. expectedTail := int64(5)
  936. setPodByNameFunc(fw, podNamespace, podName, expectedContainerName)
  937. setGetContainerLogsFunc(fw, t, expectedPodName, expectedContainerName, &v1.PodLogOptions{TailLines: &expectedTail}, output)
  938. resp, err := http.Get(fw.testHTTPServer.URL + "/containerLogs/" + podNamespace + "/" + podName + "/" + expectedContainerName + "?tailLines=5")
  939. if err != nil {
  940. t.Errorf("Got error GETing: %v", err)
  941. }
  942. defer resp.Body.Close()
  943. body, err := ioutil.ReadAll(resp.Body)
  944. if err != nil {
  945. t.Errorf("Error reading container logs: %v", err)
  946. }
  947. result := string(body)
  948. if result != output {
  949. t.Errorf("Expected: '%v', got: '%v'", output, result)
  950. }
  951. }
  952. func TestContainerLogsWithLegacyTail(t *testing.T) {
  953. fw := newServerTest()
  954. defer fw.testHTTPServer.Close()
  955. output := "foo bar"
  956. podNamespace := "other"
  957. podName := "foo"
  958. expectedPodName := getPodName(podName, podNamespace)
  959. expectedContainerName := "baz"
  960. expectedTail := int64(5)
  961. setPodByNameFunc(fw, podNamespace, podName, expectedContainerName)
  962. setGetContainerLogsFunc(fw, t, expectedPodName, expectedContainerName, &v1.PodLogOptions{TailLines: &expectedTail}, output)
  963. resp, err := http.Get(fw.testHTTPServer.URL + "/containerLogs/" + podNamespace + "/" + podName + "/" + expectedContainerName + "?tail=5")
  964. if err != nil {
  965. t.Errorf("Got error GETing: %v", err)
  966. }
  967. defer resp.Body.Close()
  968. body, err := ioutil.ReadAll(resp.Body)
  969. if err != nil {
  970. t.Errorf("Error reading container logs: %v", err)
  971. }
  972. result := string(body)
  973. if result != output {
  974. t.Errorf("Expected: '%v', got: '%v'", output, result)
  975. }
  976. }
  977. func TestContainerLogsWithTailAll(t *testing.T) {
  978. fw := newServerTest()
  979. defer fw.testHTTPServer.Close()
  980. output := "foo bar"
  981. podNamespace := "other"
  982. podName := "foo"
  983. expectedPodName := getPodName(podName, podNamespace)
  984. expectedContainerName := "baz"
  985. setPodByNameFunc(fw, podNamespace, podName, expectedContainerName)
  986. setGetContainerLogsFunc(fw, t, expectedPodName, expectedContainerName, &v1.PodLogOptions{}, output)
  987. resp, err := http.Get(fw.testHTTPServer.URL + "/containerLogs/" + podNamespace + "/" + podName + "/" + expectedContainerName + "?tail=all")
  988. if err != nil {
  989. t.Errorf("Got error GETing: %v", err)
  990. }
  991. defer resp.Body.Close()
  992. body, err := ioutil.ReadAll(resp.Body)
  993. if err != nil {
  994. t.Errorf("Error reading container logs: %v", err)
  995. }
  996. result := string(body)
  997. if result != output {
  998. t.Errorf("Expected: '%v', got: '%v'", output, result)
  999. }
  1000. }
  1001. func TestContainerLogsWithInvalidTail(t *testing.T) {
  1002. fw := newServerTest()
  1003. defer fw.testHTTPServer.Close()
  1004. output := "foo bar"
  1005. podNamespace := "other"
  1006. podName := "foo"
  1007. expectedPodName := getPodName(podName, podNamespace)
  1008. expectedContainerName := "baz"
  1009. setPodByNameFunc(fw, podNamespace, podName, expectedContainerName)
  1010. setGetContainerLogsFunc(fw, t, expectedPodName, expectedContainerName, &v1.PodLogOptions{}, output)
  1011. resp, err := http.Get(fw.testHTTPServer.URL + "/containerLogs/" + podNamespace + "/" + podName + "/" + expectedContainerName + "?tail=-1")
  1012. if err != nil {
  1013. t.Errorf("Got error GETing: %v", err)
  1014. }
  1015. defer resp.Body.Close()
  1016. if resp.StatusCode != http.StatusUnprocessableEntity {
  1017. t.Errorf("Unexpected non-error reading container logs: %#v", resp)
  1018. }
  1019. }
  1020. func TestContainerLogsWithFollow(t *testing.T) {
  1021. fw := newServerTest()
  1022. defer fw.testHTTPServer.Close()
  1023. output := "foo bar"
  1024. podNamespace := "other"
  1025. podName := "foo"
  1026. expectedPodName := getPodName(podName, podNamespace)
  1027. expectedContainerName := "baz"
  1028. setPodByNameFunc(fw, podNamespace, podName, expectedContainerName)
  1029. setGetContainerLogsFunc(fw, t, expectedPodName, expectedContainerName, &v1.PodLogOptions{Follow: true}, output)
  1030. resp, err := http.Get(fw.testHTTPServer.URL + "/containerLogs/" + podNamespace + "/" + podName + "/" + expectedContainerName + "?follow=1")
  1031. if err != nil {
  1032. t.Errorf("Got error GETing: %v", err)
  1033. }
  1034. defer resp.Body.Close()
  1035. body, err := ioutil.ReadAll(resp.Body)
  1036. if err != nil {
  1037. t.Errorf("Error reading container logs: %v", err)
  1038. }
  1039. result := string(body)
  1040. if result != output {
  1041. t.Errorf("Expected: '%v', got: '%v'", output, result)
  1042. }
  1043. }
  1044. func TestServeExecInContainerIdleTimeout(t *testing.T) {
  1045. ss, err := newTestStreamingServer(100 * time.Millisecond)
  1046. require.NoError(t, err)
  1047. defer ss.testHTTPServer.Close()
  1048. fw := newServerTestWithDebug(true, false, ss)
  1049. defer fw.testHTTPServer.Close()
  1050. podNamespace := "other"
  1051. podName := "foo"
  1052. expectedContainerName := "baz"
  1053. url := fw.testHTTPServer.URL + "/exec/" + podNamespace + "/" + podName + "/" + expectedContainerName + "?c=ls&c=-a&" + api.ExecStdinParam + "=1"
  1054. upgradeRoundTripper := spdy.NewSpdyRoundTripper(nil, true, true)
  1055. c := &http.Client{Transport: upgradeRoundTripper}
  1056. resp, err := c.Post(url, "", nil)
  1057. if err != nil {
  1058. t.Fatalf("Got error POSTing: %v", err)
  1059. }
  1060. defer resp.Body.Close()
  1061. upgradeRoundTripper.Dialer = &net.Dialer{
  1062. Deadline: time.Now().Add(60 * time.Second),
  1063. Timeout: 60 * time.Second,
  1064. }
  1065. conn, err := upgradeRoundTripper.NewConnection(resp)
  1066. if err != nil {
  1067. t.Fatalf("Unexpected error creating streaming connection: %s", err)
  1068. }
  1069. if conn == nil {
  1070. t.Fatal("Unexpected nil connection")
  1071. }
  1072. <-conn.CloseChan()
  1073. }
  1074. func testExecAttach(t *testing.T, verb string) {
  1075. tests := map[string]struct {
  1076. stdin bool
  1077. stdout bool
  1078. stderr bool
  1079. tty bool
  1080. responseStatusCode int
  1081. uid bool
  1082. redirect bool
  1083. }{
  1084. "no input or output": {responseStatusCode: http.StatusBadRequest},
  1085. "stdin": {stdin: true, responseStatusCode: http.StatusSwitchingProtocols},
  1086. "stdout": {stdout: true, responseStatusCode: http.StatusSwitchingProtocols},
  1087. "stderr": {stderr: true, responseStatusCode: http.StatusSwitchingProtocols},
  1088. "stdout and stderr": {stdout: true, stderr: true, responseStatusCode: http.StatusSwitchingProtocols},
  1089. "stdout stderr and tty": {stdout: true, stderr: true, tty: true, responseStatusCode: http.StatusSwitchingProtocols},
  1090. "stdin stdout and stderr": {stdin: true, stdout: true, stderr: true, responseStatusCode: http.StatusSwitchingProtocols},
  1091. "stdin stdout stderr with uid": {stdin: true, stdout: true, stderr: true, responseStatusCode: http.StatusSwitchingProtocols, uid: true},
  1092. "stdout with redirect": {stdout: true, responseStatusCode: http.StatusFound, redirect: true},
  1093. }
  1094. for desc, test := range tests {
  1095. test := test
  1096. t.Run(desc, func(t *testing.T) {
  1097. ss, err := newTestStreamingServer(0)
  1098. require.NoError(t, err)
  1099. defer ss.testHTTPServer.Close()
  1100. fw := newServerTestWithDebug(true, test.redirect, ss)
  1101. defer fw.testHTTPServer.Close()
  1102. fmt.Println(desc)
  1103. podNamespace := "other"
  1104. podName := "foo"
  1105. expectedPodName := getPodName(podName, podNamespace)
  1106. expectedContainerName := "baz"
  1107. expectedCommand := "ls -a"
  1108. expectedStdin := "stdin"
  1109. expectedStdout := "stdout"
  1110. expectedStderr := "stderr"
  1111. done := make(chan struct{})
  1112. clientStdoutReadDone := make(chan struct{})
  1113. clientStderrReadDone := make(chan struct{})
  1114. execInvoked := false
  1115. attachInvoked := false
  1116. checkStream := func(podFullName string, uid types.UID, containerName string, streamOpts remotecommandserver.Options) {
  1117. assert.Equal(t, expectedPodName, podFullName, "podFullName")
  1118. if test.uid {
  1119. assert.Equal(t, testUID, string(uid), "uid")
  1120. }
  1121. assert.Equal(t, expectedContainerName, containerName, "containerName")
  1122. assert.Equal(t, test.stdin, streamOpts.Stdin, "stdin")
  1123. assert.Equal(t, test.stdout, streamOpts.Stdout, "stdout")
  1124. assert.Equal(t, test.tty, streamOpts.TTY, "tty")
  1125. assert.Equal(t, !test.tty && test.stderr, streamOpts.Stderr, "stderr")
  1126. }
  1127. fw.fakeKubelet.getExecCheck = func(podFullName string, uid types.UID, containerName string, cmd []string, streamOpts remotecommandserver.Options) {
  1128. execInvoked = true
  1129. assert.Equal(t, expectedCommand, strings.Join(cmd, " "), "cmd")
  1130. checkStream(podFullName, uid, containerName, streamOpts)
  1131. }
  1132. fw.fakeKubelet.getAttachCheck = func(podFullName string, uid types.UID, containerName string, streamOpts remotecommandserver.Options) {
  1133. attachInvoked = true
  1134. checkStream(podFullName, uid, containerName, streamOpts)
  1135. }
  1136. testStream := func(containerID string, in io.Reader, out, stderr io.WriteCloser, tty bool, done chan struct{}) error {
  1137. close(done)
  1138. assert.Equal(t, testContainerID, containerID, "containerID")
  1139. assert.Equal(t, test.tty, tty, "tty")
  1140. require.Equal(t, test.stdin, in != nil, "in")
  1141. require.Equal(t, test.stdout, out != nil, "out")
  1142. require.Equal(t, !test.tty && test.stderr, stderr != nil, "err")
  1143. if test.stdin {
  1144. b := make([]byte, 10)
  1145. n, err := in.Read(b)
  1146. assert.NoError(t, err, "reading from stdin")
  1147. assert.Equal(t, expectedStdin, string(b[0:n]), "content from stdin")
  1148. }
  1149. if test.stdout {
  1150. _, err := out.Write([]byte(expectedStdout))
  1151. assert.NoError(t, err, "writing to stdout")
  1152. out.Close()
  1153. <-clientStdoutReadDone
  1154. }
  1155. if !test.tty && test.stderr {
  1156. _, err := stderr.Write([]byte(expectedStderr))
  1157. assert.NoError(t, err, "writing to stderr")
  1158. stderr.Close()
  1159. <-clientStderrReadDone
  1160. }
  1161. return nil
  1162. }
  1163. ss.fakeRuntime.execFunc = func(containerID string, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize) error {
  1164. assert.Equal(t, expectedCommand, strings.Join(cmd, " "), "cmd")
  1165. return testStream(containerID, stdin, stdout, stderr, tty, done)
  1166. }
  1167. ss.fakeRuntime.attachFunc = func(containerID string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize) error {
  1168. return testStream(containerID, stdin, stdout, stderr, tty, done)
  1169. }
  1170. var url string
  1171. if test.uid {
  1172. url = fw.testHTTPServer.URL + "/" + verb + "/" + podNamespace + "/" + podName + "/" + testUID + "/" + expectedContainerName + "?ignore=1"
  1173. } else {
  1174. url = fw.testHTTPServer.URL + "/" + verb + "/" + podNamespace + "/" + podName + "/" + expectedContainerName + "?ignore=1"
  1175. }
  1176. if verb == "exec" {
  1177. url += "&command=ls&command=-a"
  1178. }
  1179. if test.stdin {
  1180. url += "&" + api.ExecStdinParam + "=1"
  1181. }
  1182. if test.stdout {
  1183. url += "&" + api.ExecStdoutParam + "=1"
  1184. }
  1185. if test.stderr && !test.tty {
  1186. url += "&" + api.ExecStderrParam + "=1"
  1187. }
  1188. if test.tty {
  1189. url += "&" + api.ExecTTYParam + "=1"
  1190. }
  1191. var (
  1192. resp *http.Response
  1193. upgradeRoundTripper httpstream.UpgradeRoundTripper
  1194. c *http.Client
  1195. )
  1196. if test.redirect {
  1197. c = &http.Client{}
  1198. // Don't follow redirects, since we want to inspect the redirect response.
  1199. c.CheckRedirect = func(*http.Request, []*http.Request) error {
  1200. return http.ErrUseLastResponse
  1201. }
  1202. } else {
  1203. upgradeRoundTripper = spdy.NewRoundTripper(nil, true, true)
  1204. c = &http.Client{Transport: upgradeRoundTripper}
  1205. }
  1206. resp, err = c.Post(url, "", nil)
  1207. require.NoError(t, err, "POSTing")
  1208. defer resp.Body.Close()
  1209. _, err = ioutil.ReadAll(resp.Body)
  1210. assert.NoError(t, err, "reading response body")
  1211. require.Equal(t, test.responseStatusCode, resp.StatusCode, "response status")
  1212. if test.responseStatusCode != http.StatusSwitchingProtocols {
  1213. return
  1214. }
  1215. conn, err := upgradeRoundTripper.NewConnection(resp)
  1216. require.NoError(t, err, "creating streaming connection")
  1217. defer conn.Close()
  1218. h := http.Header{}
  1219. h.Set(api.StreamType, api.StreamTypeError)
  1220. _, err = conn.CreateStream(h)
  1221. require.NoError(t, err, "creating error stream")
  1222. if test.stdin {
  1223. h.Set(api.StreamType, api.StreamTypeStdin)
  1224. stream, err := conn.CreateStream(h)
  1225. require.NoError(t, err, "creating stdin stream")
  1226. _, err = stream.Write([]byte(expectedStdin))
  1227. require.NoError(t, err, "writing to stdin stream")
  1228. }
  1229. var stdoutStream httpstream.Stream
  1230. if test.stdout {
  1231. h.Set(api.StreamType, api.StreamTypeStdout)
  1232. stdoutStream, err = conn.CreateStream(h)
  1233. require.NoError(t, err, "creating stdout stream")
  1234. }
  1235. var stderrStream httpstream.Stream
  1236. if test.stderr && !test.tty {
  1237. h.Set(api.StreamType, api.StreamTypeStderr)
  1238. stderrStream, err = conn.CreateStream(h)
  1239. require.NoError(t, err, "creating stderr stream")
  1240. }
  1241. if test.stdout {
  1242. output := make([]byte, 10)
  1243. n, err := stdoutStream.Read(output)
  1244. close(clientStdoutReadDone)
  1245. assert.NoError(t, err, "reading from stdout stream")
  1246. assert.Equal(t, expectedStdout, string(output[0:n]), "stdout")
  1247. }
  1248. if test.stderr && !test.tty {
  1249. output := make([]byte, 10)
  1250. n, err := stderrStream.Read(output)
  1251. close(clientStderrReadDone)
  1252. assert.NoError(t, err, "reading from stderr stream")
  1253. assert.Equal(t, expectedStderr, string(output[0:n]), "stderr")
  1254. }
  1255. // wait for the server to finish before checking if the attach/exec funcs were invoked
  1256. <-done
  1257. if verb == "exec" {
  1258. assert.True(t, execInvoked, "exec should be invoked")
  1259. assert.False(t, attachInvoked, "attach should not be invoked")
  1260. } else {
  1261. assert.True(t, attachInvoked, "attach should be invoked")
  1262. assert.False(t, execInvoked, "exec should not be invoked")
  1263. }
  1264. })
  1265. }
  1266. }
  1267. func TestServeExecInContainer(t *testing.T) {
  1268. testExecAttach(t, "exec")
  1269. }
  1270. func TestServeAttachContainer(t *testing.T) {
  1271. testExecAttach(t, "attach")
  1272. }
  1273. func TestServePortForwardIdleTimeout(t *testing.T) {
  1274. ss, err := newTestStreamingServer(100 * time.Millisecond)
  1275. require.NoError(t, err)
  1276. defer ss.testHTTPServer.Close()
  1277. fw := newServerTestWithDebug(true, false, ss)
  1278. defer fw.testHTTPServer.Close()
  1279. podNamespace := "other"
  1280. podName := "foo"
  1281. url := fw.testHTTPServer.URL + "/portForward/" + podNamespace + "/" + podName
  1282. upgradeRoundTripper := spdy.NewRoundTripper(nil, true, true)
  1283. c := &http.Client{Transport: upgradeRoundTripper}
  1284. resp, err := c.Post(url, "", nil)
  1285. if err != nil {
  1286. t.Fatalf("Got error POSTing: %v", err)
  1287. }
  1288. defer resp.Body.Close()
  1289. conn, err := upgradeRoundTripper.NewConnection(resp)
  1290. if err != nil {
  1291. t.Fatalf("Unexpected error creating streaming connection: %s", err)
  1292. }
  1293. if conn == nil {
  1294. t.Fatal("Unexpected nil connection")
  1295. }
  1296. defer conn.Close()
  1297. <-conn.CloseChan()
  1298. }
  1299. func TestServePortForward(t *testing.T) {
  1300. tests := map[string]struct {
  1301. port string
  1302. uid bool
  1303. clientData string
  1304. containerData string
  1305. redirect bool
  1306. shouldError bool
  1307. }{
  1308. "no port": {port: "", shouldError: true},
  1309. "none number port": {port: "abc", shouldError: true},
  1310. "negative port": {port: "-1", shouldError: true},
  1311. "too large port": {port: "65536", shouldError: true},
  1312. "0 port": {port: "0", shouldError: true},
  1313. "min port": {port: "1", shouldError: false},
  1314. "normal port": {port: "8000", shouldError: false},
  1315. "normal port with data forward": {port: "8000", clientData: "client data", containerData: "container data", shouldError: false},
  1316. "max port": {port: "65535", shouldError: false},
  1317. "normal port with uid": {port: "8000", uid: true, shouldError: false},
  1318. "normal port with redirect": {port: "8000", redirect: true, shouldError: false},
  1319. }
  1320. podNamespace := "other"
  1321. podName := "foo"
  1322. for desc, test := range tests {
  1323. test := test
  1324. t.Run(desc, func(t *testing.T) {
  1325. ss, err := newTestStreamingServer(0)
  1326. require.NoError(t, err)
  1327. defer ss.testHTTPServer.Close()
  1328. fw := newServerTestWithDebug(true, test.redirect, ss)
  1329. defer fw.testHTTPServer.Close()
  1330. portForwardFuncDone := make(chan struct{})
  1331. fw.fakeKubelet.getPortForwardCheck = func(name, namespace string, uid types.UID, opts portforward.V4Options) {
  1332. assert.Equal(t, podName, name, "pod name")
  1333. assert.Equal(t, podNamespace, namespace, "pod namespace")
  1334. if test.uid {
  1335. assert.Equal(t, testUID, string(uid), "uid")
  1336. }
  1337. }
  1338. ss.fakeRuntime.portForwardFunc = func(podSandboxID string, port int32, stream io.ReadWriteCloser) error {
  1339. defer close(portForwardFuncDone)
  1340. assert.Equal(t, testPodSandboxID, podSandboxID, "pod sandbox id")
  1341. // The port should be valid if it reaches here.
  1342. testPort, err := strconv.ParseInt(test.port, 10, 32)
  1343. require.NoError(t, err, "parse port")
  1344. assert.Equal(t, int32(testPort), port, "port")
  1345. if test.clientData != "" {
  1346. fromClient := make([]byte, 32)
  1347. n, err := stream.Read(fromClient)
  1348. assert.NoError(t, err, "reading client data")
  1349. assert.Equal(t, test.clientData, string(fromClient[0:n]), "client data")
  1350. }
  1351. if test.containerData != "" {
  1352. _, err := stream.Write([]byte(test.containerData))
  1353. assert.NoError(t, err, "writing container data")
  1354. }
  1355. return nil
  1356. }
  1357. var url string
  1358. if test.uid {
  1359. url = fmt.Sprintf("%s/portForward/%s/%s/%s", fw.testHTTPServer.URL, podNamespace, podName, testUID)
  1360. } else {
  1361. url = fmt.Sprintf("%s/portForward/%s/%s", fw.testHTTPServer.URL, podNamespace, podName)
  1362. }
  1363. var (
  1364. upgradeRoundTripper httpstream.UpgradeRoundTripper
  1365. c *http.Client
  1366. )
  1367. if test.redirect {
  1368. c = &http.Client{}
  1369. // Don't follow redirects, since we want to inspect the redirect response.
  1370. c.CheckRedirect = func(*http.Request, []*http.Request) error {
  1371. return http.ErrUseLastResponse
  1372. }
  1373. } else {
  1374. upgradeRoundTripper = spdy.NewRoundTripper(nil, true, true)
  1375. c = &http.Client{Transport: upgradeRoundTripper}
  1376. }
  1377. resp, err := c.Post(url, "", nil)
  1378. require.NoError(t, err, "POSTing")
  1379. defer resp.Body.Close()
  1380. if test.redirect {
  1381. assert.Equal(t, http.StatusFound, resp.StatusCode, "status code")
  1382. return
  1383. }
  1384. assert.Equal(t, http.StatusSwitchingProtocols, resp.StatusCode, "status code")
  1385. conn, err := upgradeRoundTripper.NewConnection(resp)
  1386. require.NoError(t, err, "creating streaming connection")
  1387. defer conn.Close()
  1388. headers := http.Header{}
  1389. headers.Set("streamType", "error")
  1390. headers.Set("port", test.port)
  1391. _, err = conn.CreateStream(headers)
  1392. assert.Equal(t, test.shouldError, err != nil, "expect error")
  1393. if test.shouldError {
  1394. return
  1395. }
  1396. headers.Set("streamType", "data")
  1397. headers.Set("port", test.port)
  1398. dataStream, err := conn.CreateStream(headers)
  1399. require.NoError(t, err, "create stream")
  1400. if test.clientData != "" {
  1401. _, err := dataStream.Write([]byte(test.clientData))
  1402. assert.NoError(t, err, "writing client data")
  1403. }
  1404. if test.containerData != "" {
  1405. fromContainer := make([]byte, 32)
  1406. n, err := dataStream.Read(fromContainer)
  1407. assert.NoError(t, err, "reading container data")
  1408. assert.Equal(t, test.containerData, string(fromContainer[0:n]), "container data")
  1409. }
  1410. <-portForwardFuncDone
  1411. })
  1412. }
  1413. }
  1414. func TestCRIHandler(t *testing.T) {
  1415. fw := newServerTest()
  1416. defer fw.testHTTPServer.Close()
  1417. const (
  1418. path = "/cri/exec/123456abcdef"
  1419. query = "cmd=echo+foo"
  1420. )
  1421. resp, err := http.Get(fw.testHTTPServer.URL + path + "?" + query)
  1422. require.NoError(t, err)
  1423. assert.Equal(t, http.StatusOK, resp.StatusCode)
  1424. assert.Equal(t, "GET", fw.criHandler.RequestReceived.Method)
  1425. assert.Equal(t, path, fw.criHandler.RequestReceived.URL.Path)
  1426. assert.Equal(t, query, fw.criHandler.RequestReceived.URL.RawQuery)
  1427. }
  1428. func TestDebuggingDisabledHandlers(t *testing.T) {
  1429. fw := newServerTestWithDebug(false, false, nil)
  1430. defer fw.testHTTPServer.Close()
  1431. paths := []string{
  1432. "/run", "/exec", "/attach", "/portForward", "/containerLogs", "/runningpods",
  1433. "/run/", "/exec/", "/attach/", "/portForward/", "/containerLogs/", "/runningpods/",
  1434. "/run/xxx", "/exec/xxx", "/attach/xxx", "/debug/pprof/profile", "/logs/kubelet.log",
  1435. }
  1436. for _, p := range paths {
  1437. resp, err := http.Get(fw.testHTTPServer.URL + p)
  1438. require.NoError(t, err)
  1439. assert.Equal(t, http.StatusMethodNotAllowed, resp.StatusCode)
  1440. body, err := ioutil.ReadAll(resp.Body)
  1441. require.NoError(t, err)
  1442. assert.Equal(t, "Debug endpoints are disabled.\n", string(body))
  1443. resp, err = http.Post(fw.testHTTPServer.URL+p, "", nil)
  1444. require.NoError(t, err)
  1445. assert.Equal(t, http.StatusMethodNotAllowed, resp.StatusCode)
  1446. body, err = ioutil.ReadAll(resp.Body)
  1447. require.NoError(t, err)
  1448. assert.Equal(t, "Debug endpoints are disabled.\n", string(body))
  1449. }
  1450. // test some other paths, make sure they're working
  1451. containerInfo := &cadvisorapi.ContainerInfo{
  1452. ContainerReference: cadvisorapi.ContainerReference{
  1453. Name: "/",
  1454. },
  1455. }
  1456. fw.fakeKubelet.rawInfoFunc = func(req *cadvisorapi.ContainerInfoRequest) (map[string]*cadvisorapi.ContainerInfo, error) {
  1457. return map[string]*cadvisorapi.ContainerInfo{
  1458. containerInfo.Name: containerInfo,
  1459. }, nil
  1460. }
  1461. resp, err := http.Get(fw.testHTTPServer.URL + "/stats")
  1462. require.NoError(t, err)
  1463. assert.Equal(t, http.StatusOK, resp.StatusCode)
  1464. machineInfo := &cadvisorapi.MachineInfo{
  1465. NumCores: 4,
  1466. MemoryCapacity: 1024,
  1467. }
  1468. fw.fakeKubelet.machineInfoFunc = func() (*cadvisorapi.MachineInfo, error) {
  1469. return machineInfo, nil
  1470. }
  1471. resp, err = http.Get(fw.testHTTPServer.URL + "/spec")
  1472. require.NoError(t, err)
  1473. assert.Equal(t, http.StatusOK, resp.StatusCode)
  1474. }
  1475. func TestTrimURLPath(t *testing.T) {
  1476. tests := []struct {
  1477. path, expected string
  1478. }{
  1479. {"", ""},
  1480. {"//", ""},
  1481. {"/pods", "pods"},
  1482. {"pods", "pods"},
  1483. {"pods/", "pods"},
  1484. {"good/", "good"},
  1485. {"pods/probes", "pods"},
  1486. {"metrics", "metrics"},
  1487. {"metrics/resource", "metrics/resource"},
  1488. {"metrics/hello", "metrics/hello"},
  1489. }
  1490. for _, test := range tests {
  1491. assert.Equal(t, test.expected, trimURLPath(test.path), fmt.Sprintf("path is: %s", test.path))
  1492. }
  1493. }