strategy_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  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 pod
  14. import (
  15. "context"
  16. "net/url"
  17. "reflect"
  18. "testing"
  19. "k8s.io/apimachinery/pkg/api/errors"
  20. "k8s.io/apimachinery/pkg/api/resource"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. "k8s.io/apimachinery/pkg/fields"
  23. "k8s.io/apimachinery/pkg/labels"
  24. "k8s.io/apimachinery/pkg/runtime"
  25. "k8s.io/apimachinery/pkg/types"
  26. genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
  27. apitesting "k8s.io/kubernetes/pkg/api/testing"
  28. api "k8s.io/kubernetes/pkg/apis/core"
  29. "k8s.io/kubernetes/pkg/kubelet/client"
  30. // install all api groups for testing
  31. _ "k8s.io/kubernetes/pkg/api/testapi"
  32. )
  33. func TestMatchPod(t *testing.T) {
  34. testCases := []struct {
  35. in *api.Pod
  36. fieldSelector fields.Selector
  37. expectMatch bool
  38. }{
  39. {
  40. in: &api.Pod{
  41. Spec: api.PodSpec{NodeName: "nodeA"},
  42. },
  43. fieldSelector: fields.ParseSelectorOrDie("spec.nodeName=nodeA"),
  44. expectMatch: true,
  45. },
  46. {
  47. in: &api.Pod{
  48. Spec: api.PodSpec{NodeName: "nodeB"},
  49. },
  50. fieldSelector: fields.ParseSelectorOrDie("spec.nodeName=nodeA"),
  51. expectMatch: false,
  52. },
  53. {
  54. in: &api.Pod{
  55. Spec: api.PodSpec{RestartPolicy: api.RestartPolicyAlways},
  56. },
  57. fieldSelector: fields.ParseSelectorOrDie("spec.restartPolicy=Always"),
  58. expectMatch: true,
  59. },
  60. {
  61. in: &api.Pod{
  62. Spec: api.PodSpec{RestartPolicy: api.RestartPolicyAlways},
  63. },
  64. fieldSelector: fields.ParseSelectorOrDie("spec.restartPolicy=Never"),
  65. expectMatch: false,
  66. },
  67. {
  68. in: &api.Pod{
  69. Spec: api.PodSpec{SchedulerName: "scheduler1"},
  70. },
  71. fieldSelector: fields.ParseSelectorOrDie("spec.schedulerName=scheduler1"),
  72. expectMatch: true,
  73. },
  74. {
  75. in: &api.Pod{
  76. Spec: api.PodSpec{SchedulerName: "scheduler1"},
  77. },
  78. fieldSelector: fields.ParseSelectorOrDie("spec.schedulerName=scheduler2"),
  79. expectMatch: false,
  80. },
  81. {
  82. in: &api.Pod{
  83. Spec: api.PodSpec{ServiceAccountName: "serviceAccount1"},
  84. },
  85. fieldSelector: fields.ParseSelectorOrDie("spec.serviceAccountName=serviceAccount1"),
  86. expectMatch: true,
  87. },
  88. {
  89. in: &api.Pod{
  90. Spec: api.PodSpec{SchedulerName: "serviceAccount1"},
  91. },
  92. fieldSelector: fields.ParseSelectorOrDie("spec.serviceAccountName=serviceAccount2"),
  93. expectMatch: false,
  94. },
  95. {
  96. in: &api.Pod{
  97. Status: api.PodStatus{Phase: api.PodRunning},
  98. },
  99. fieldSelector: fields.ParseSelectorOrDie("status.phase=Running"),
  100. expectMatch: true,
  101. },
  102. {
  103. in: &api.Pod{
  104. Status: api.PodStatus{Phase: api.PodRunning},
  105. },
  106. fieldSelector: fields.ParseSelectorOrDie("status.phase=Pending"),
  107. expectMatch: false,
  108. },
  109. {
  110. in: &api.Pod{
  111. Status: api.PodStatus{PodIP: "1.2.3.4"},
  112. },
  113. fieldSelector: fields.ParseSelectorOrDie("status.podIP=1.2.3.4"),
  114. expectMatch: true,
  115. },
  116. {
  117. in: &api.Pod{
  118. Status: api.PodStatus{PodIP: "1.2.3.4"},
  119. },
  120. fieldSelector: fields.ParseSelectorOrDie("status.podIP=4.3.2.1"),
  121. expectMatch: false,
  122. },
  123. {
  124. in: &api.Pod{
  125. Status: api.PodStatus{NominatedNodeName: "node1"},
  126. },
  127. fieldSelector: fields.ParseSelectorOrDie("status.nominatedNodeName=node1"),
  128. expectMatch: true,
  129. },
  130. {
  131. in: &api.Pod{
  132. Status: api.PodStatus{NominatedNodeName: "node1"},
  133. },
  134. fieldSelector: fields.ParseSelectorOrDie("status.nominatedNodeName=node2"),
  135. expectMatch: false,
  136. }}
  137. for _, testCase := range testCases {
  138. m := MatchPod(labels.Everything(), testCase.fieldSelector)
  139. result, err := m.Matches(testCase.in)
  140. if err != nil {
  141. t.Errorf("Unexpected error %v", err)
  142. }
  143. if result != testCase.expectMatch {
  144. t.Errorf("Result %v, Expected %v, Selector: %v, Pod: %v", result, testCase.expectMatch, testCase.fieldSelector.String(), testCase.in)
  145. }
  146. }
  147. }
  148. func getResourceList(cpu, memory string) api.ResourceList {
  149. res := api.ResourceList{}
  150. if cpu != "" {
  151. res[api.ResourceCPU] = resource.MustParse(cpu)
  152. }
  153. if memory != "" {
  154. res[api.ResourceMemory] = resource.MustParse(memory)
  155. }
  156. return res
  157. }
  158. func getResourceRequirements(requests, limits api.ResourceList) api.ResourceRequirements {
  159. res := api.ResourceRequirements{}
  160. res.Requests = requests
  161. res.Limits = limits
  162. return res
  163. }
  164. func newContainer(name string, requests api.ResourceList, limits api.ResourceList) api.Container {
  165. return api.Container{
  166. Name: name,
  167. Resources: getResourceRequirements(requests, limits),
  168. }
  169. }
  170. func newPod(name string, containers []api.Container) *api.Pod {
  171. return &api.Pod{
  172. ObjectMeta: metav1.ObjectMeta{
  173. Name: name,
  174. },
  175. Spec: api.PodSpec{
  176. Containers: containers,
  177. },
  178. }
  179. }
  180. func TestGetPodQOS(t *testing.T) {
  181. testCases := []struct {
  182. pod *api.Pod
  183. expected api.PodQOSClass
  184. }{
  185. {
  186. pod: newPod("guaranteed", []api.Container{
  187. newContainer("guaranteed", getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")),
  188. }),
  189. expected: api.PodQOSGuaranteed,
  190. },
  191. {
  192. pod: newPod("best-effort", []api.Container{
  193. newContainer("best-effort", getResourceList("", ""), getResourceList("", "")),
  194. }),
  195. expected: api.PodQOSBestEffort,
  196. },
  197. {
  198. pod: newPod("burstable", []api.Container{
  199. newContainer("burstable", getResourceList("100m", "100Mi"), getResourceList("", "")),
  200. }),
  201. expected: api.PodQOSBurstable,
  202. },
  203. }
  204. for id, testCase := range testCases {
  205. Strategy.PrepareForCreate(genericapirequest.NewContext(), testCase.pod)
  206. actual := testCase.pod.Status.QOSClass
  207. if actual != testCase.expected {
  208. t.Errorf("[%d]: invalid qos pod %s, expected: %s, actual: %s", id, testCase.pod.Name, testCase.expected, actual)
  209. }
  210. }
  211. }
  212. func TestCheckGracefulDelete(t *testing.T) {
  213. defaultGracePeriod := int64(30)
  214. tcs := []struct {
  215. in *api.Pod
  216. gracePeriod int64
  217. }{
  218. {
  219. in: &api.Pod{
  220. Spec: api.PodSpec{NodeName: "something"},
  221. Status: api.PodStatus{Phase: api.PodPending},
  222. },
  223. gracePeriod: defaultGracePeriod,
  224. },
  225. {
  226. in: &api.Pod{
  227. Spec: api.PodSpec{NodeName: "something"},
  228. Status: api.PodStatus{Phase: api.PodFailed},
  229. },
  230. gracePeriod: 0,
  231. },
  232. {
  233. in: &api.Pod{
  234. Spec: api.PodSpec{},
  235. Status: api.PodStatus{Phase: api.PodPending},
  236. },
  237. gracePeriod: 0,
  238. },
  239. {
  240. in: &api.Pod{
  241. Spec: api.PodSpec{},
  242. Status: api.PodStatus{Phase: api.PodSucceeded},
  243. },
  244. gracePeriod: 0,
  245. },
  246. {
  247. in: &api.Pod{
  248. Spec: api.PodSpec{},
  249. Status: api.PodStatus{},
  250. },
  251. gracePeriod: 0,
  252. },
  253. }
  254. for _, tc := range tcs {
  255. out := &metav1.DeleteOptions{GracePeriodSeconds: &defaultGracePeriod}
  256. Strategy.CheckGracefulDelete(genericapirequest.NewContext(), tc.in, out)
  257. if out.GracePeriodSeconds == nil {
  258. t.Errorf("out grace period was nil but supposed to be %v", tc.gracePeriod)
  259. }
  260. if *(out.GracePeriodSeconds) != tc.gracePeriod {
  261. t.Errorf("out grace period was %v but was expected to be %v", *out, tc.gracePeriod)
  262. }
  263. }
  264. }
  265. type mockPodGetter struct {
  266. pod *api.Pod
  267. }
  268. func (g mockPodGetter) Get(context.Context, string, *metav1.GetOptions) (runtime.Object, error) {
  269. return g.pod, nil
  270. }
  271. func TestCheckLogLocation(t *testing.T) {
  272. ctx := genericapirequest.NewDefaultContext()
  273. tcs := []struct {
  274. in *api.Pod
  275. opts *api.PodLogOptions
  276. expectedErr error
  277. }{
  278. {
  279. in: &api.Pod{
  280. Spec: api.PodSpec{},
  281. Status: api.PodStatus{},
  282. },
  283. opts: &api.PodLogOptions{},
  284. expectedErr: errors.NewBadRequest("a container name must be specified for pod test"),
  285. },
  286. {
  287. in: &api.Pod{
  288. Spec: api.PodSpec{
  289. Containers: []api.Container{
  290. {Name: "mycontainer"},
  291. },
  292. },
  293. Status: api.PodStatus{},
  294. },
  295. opts: &api.PodLogOptions{},
  296. expectedErr: nil,
  297. },
  298. {
  299. in: &api.Pod{
  300. Spec: api.PodSpec{
  301. Containers: []api.Container{
  302. {Name: "container1"},
  303. {Name: "container2"},
  304. },
  305. },
  306. Status: api.PodStatus{},
  307. },
  308. opts: &api.PodLogOptions{},
  309. expectedErr: errors.NewBadRequest("a container name must be specified for pod test, choose one of: [container1 container2]"),
  310. },
  311. {
  312. in: &api.Pod{
  313. Spec: api.PodSpec{
  314. Containers: []api.Container{
  315. {Name: "container1"},
  316. {Name: "container2"},
  317. },
  318. InitContainers: []api.Container{
  319. {Name: "initcontainer1"},
  320. },
  321. },
  322. Status: api.PodStatus{},
  323. },
  324. opts: &api.PodLogOptions{},
  325. expectedErr: errors.NewBadRequest("a container name must be specified for pod test, choose one of: [container1 container2] or one of the init containers: [initcontainer1]"),
  326. },
  327. {
  328. in: &api.Pod{
  329. Spec: api.PodSpec{
  330. Containers: []api.Container{
  331. {Name: "container1"},
  332. {Name: "container2"},
  333. },
  334. },
  335. Status: api.PodStatus{},
  336. },
  337. opts: &api.PodLogOptions{
  338. Container: "unknown",
  339. },
  340. expectedErr: errors.NewBadRequest("container unknown is not valid for pod test"),
  341. },
  342. {
  343. in: &api.Pod{
  344. Spec: api.PodSpec{
  345. Containers: []api.Container{
  346. {Name: "container1"},
  347. {Name: "container2"},
  348. },
  349. },
  350. Status: api.PodStatus{},
  351. },
  352. opts: &api.PodLogOptions{
  353. Container: "container2",
  354. },
  355. expectedErr: nil,
  356. },
  357. }
  358. for _, tc := range tcs {
  359. getter := &mockPodGetter{tc.in}
  360. _, _, err := LogLocation(getter, nil, ctx, "test", tc.opts)
  361. if !reflect.DeepEqual(err, tc.expectedErr) {
  362. t.Errorf("expected %v, got %v", tc.expectedErr, err)
  363. }
  364. }
  365. }
  366. func TestSelectableFieldLabelConversions(t *testing.T) {
  367. apitesting.TestSelectableFieldLabelConversionsOfKind(t,
  368. "v1",
  369. "Pod",
  370. PodToSelectableFields(&api.Pod{}),
  371. nil,
  372. )
  373. }
  374. type mockConnectionInfoGetter struct {
  375. info *client.ConnectionInfo
  376. }
  377. func (g mockConnectionInfoGetter) GetConnectionInfo(ctx context.Context, nodeName types.NodeName) (*client.ConnectionInfo, error) {
  378. return g.info, nil
  379. }
  380. func TestPortForwardLocation(t *testing.T) {
  381. ctx := genericapirequest.NewDefaultContext()
  382. tcs := []struct {
  383. in *api.Pod
  384. info *client.ConnectionInfo
  385. opts *api.PodPortForwardOptions
  386. expectedErr error
  387. expectedURL *url.URL
  388. }{
  389. {
  390. in: &api.Pod{
  391. Spec: api.PodSpec{},
  392. },
  393. opts: &api.PodPortForwardOptions{},
  394. expectedErr: errors.NewBadRequest("pod test does not have a host assigned"),
  395. },
  396. {
  397. in: &api.Pod{
  398. ObjectMeta: metav1.ObjectMeta{
  399. Namespace: "ns",
  400. Name: "pod1",
  401. },
  402. Spec: api.PodSpec{
  403. NodeName: "node1",
  404. },
  405. },
  406. info: &client.ConnectionInfo{},
  407. opts: &api.PodPortForwardOptions{},
  408. expectedURL: &url.URL{Host: ":", Path: "/portForward/ns/pod1"},
  409. },
  410. {
  411. in: &api.Pod{
  412. ObjectMeta: metav1.ObjectMeta{
  413. Namespace: "ns",
  414. Name: "pod1",
  415. },
  416. Spec: api.PodSpec{
  417. NodeName: "node1",
  418. },
  419. },
  420. info: &client.ConnectionInfo{},
  421. opts: &api.PodPortForwardOptions{Ports: []int32{80}},
  422. expectedURL: &url.URL{Host: ":", Path: "/portForward/ns/pod1", RawQuery: "port=80"},
  423. },
  424. }
  425. for _, tc := range tcs {
  426. getter := &mockPodGetter{tc.in}
  427. connectionGetter := &mockConnectionInfoGetter{tc.info}
  428. loc, _, err := PortForwardLocation(getter, connectionGetter, ctx, "test", tc.opts)
  429. if !reflect.DeepEqual(err, tc.expectedErr) {
  430. t.Errorf("expected %v, got %v", tc.expectedErr, err)
  431. }
  432. if !reflect.DeepEqual(loc, tc.expectedURL) {
  433. t.Errorf("expected %v, got %v", tc.expectedURL, loc)
  434. }
  435. }
  436. }