arguments_test.go 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. /*
  2. Copyright 2017 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 util
  14. import (
  15. "reflect"
  16. "sort"
  17. "testing"
  18. )
  19. func TestBuildArgumentListFromMap(t *testing.T) {
  20. var tests = []struct {
  21. name string
  22. base map[string]string
  23. overrides map[string]string
  24. expected []string
  25. }{
  26. {
  27. name: "override an argument from the base",
  28. base: map[string]string{
  29. "admission-control": "NamespaceLifecycle",
  30. "insecure-bind-address": "127.0.0.1",
  31. "allow-privileged": "true",
  32. },
  33. overrides: map[string]string{
  34. "admission-control": "NamespaceLifecycle,LimitRanger",
  35. },
  36. expected: []string{
  37. "--admission-control=NamespaceLifecycle,LimitRanger",
  38. "--allow-privileged=true",
  39. "--insecure-bind-address=127.0.0.1",
  40. },
  41. },
  42. {
  43. name: "add an argument that is not in base",
  44. base: map[string]string{
  45. "insecure-bind-address": "127.0.0.1",
  46. "allow-privileged": "true",
  47. },
  48. overrides: map[string]string{
  49. "admission-control": "NamespaceLifecycle,LimitRanger",
  50. },
  51. expected: []string{
  52. "--admission-control=NamespaceLifecycle,LimitRanger",
  53. "--allow-privileged=true",
  54. "--insecure-bind-address=127.0.0.1",
  55. },
  56. },
  57. {
  58. name: "allow empty strings in base",
  59. base: map[string]string{
  60. "insecure-bind-address": "127.0.0.1",
  61. "allow-privileged": "true",
  62. "something-that-allows-empty-string": "",
  63. },
  64. overrides: map[string]string{
  65. "admission-control": "NamespaceLifecycle,LimitRanger",
  66. },
  67. expected: []string{
  68. "--admission-control=NamespaceLifecycle,LimitRanger",
  69. "--allow-privileged=true",
  70. "--insecure-bind-address=127.0.0.1",
  71. "--something-that-allows-empty-string=",
  72. },
  73. },
  74. {
  75. name: "allow empty strings in overrides",
  76. base: map[string]string{
  77. "insecure-bind-address": "127.0.0.1",
  78. "allow-privileged": "true",
  79. "something-that-allows-empty-string": "foo",
  80. },
  81. overrides: map[string]string{
  82. "admission-control": "NamespaceLifecycle,LimitRanger",
  83. "something-that-allows-empty-string": "",
  84. },
  85. expected: []string{
  86. "--admission-control=NamespaceLifecycle,LimitRanger",
  87. "--allow-privileged=true",
  88. "--insecure-bind-address=127.0.0.1",
  89. "--something-that-allows-empty-string=",
  90. },
  91. },
  92. }
  93. for _, rt := range tests {
  94. t.Run(rt.name, func(t *testing.T) {
  95. actual := BuildArgumentListFromMap(rt.base, rt.overrides)
  96. if !reflect.DeepEqual(actual, rt.expected) {
  97. t.Errorf("failed BuildArgumentListFromMap:\nexpected:\n%v\nsaw:\n%v", rt.expected, actual)
  98. }
  99. })
  100. }
  101. }
  102. func TestParseArgumentListToMap(t *testing.T) {
  103. var tests = []struct {
  104. name string
  105. args []string
  106. expectedMap map[string]string
  107. }{
  108. {
  109. name: "normal case",
  110. args: []string{
  111. "--admission-control=NamespaceLifecycle,LimitRanger",
  112. "--insecure-bind-address=127.0.0.1",
  113. "--allow-privileged=true",
  114. },
  115. expectedMap: map[string]string{
  116. "admission-control": "NamespaceLifecycle,LimitRanger",
  117. "insecure-bind-address": "127.0.0.1",
  118. "allow-privileged": "true",
  119. },
  120. },
  121. {
  122. name: "test that feature-gates is working",
  123. args: []string{
  124. "--admission-control=NamespaceLifecycle,LimitRanger",
  125. "--insecure-bind-address=127.0.0.1",
  126. "--allow-privileged=true",
  127. "--feature-gates=EnableFoo=true,EnableBar=false",
  128. },
  129. expectedMap: map[string]string{
  130. "admission-control": "NamespaceLifecycle,LimitRanger",
  131. "insecure-bind-address": "127.0.0.1",
  132. "allow-privileged": "true",
  133. "feature-gates": "EnableFoo=true,EnableBar=false",
  134. },
  135. },
  136. {
  137. name: "test that a binary can be the first arg",
  138. args: []string{
  139. "kube-apiserver",
  140. "--admission-control=NamespaceLifecycle,LimitRanger",
  141. "--insecure-bind-address=127.0.0.1",
  142. "--allow-privileged=true",
  143. "--feature-gates=EnableFoo=true,EnableBar=false",
  144. },
  145. expectedMap: map[string]string{
  146. "admission-control": "NamespaceLifecycle,LimitRanger",
  147. "insecure-bind-address": "127.0.0.1",
  148. "allow-privileged": "true",
  149. "feature-gates": "EnableFoo=true,EnableBar=false",
  150. },
  151. },
  152. }
  153. for _, rt := range tests {
  154. t.Run(rt.name, func(t *testing.T) {
  155. actualMap := ParseArgumentListToMap(rt.args)
  156. if !reflect.DeepEqual(actualMap, rt.expectedMap) {
  157. t.Errorf("failed ParseArgumentListToMap:\nexpected:\n%v\nsaw:\n%v", rt.expectedMap, actualMap)
  158. }
  159. })
  160. }
  161. }
  162. func TestReplaceArgument(t *testing.T) {
  163. var tests = []struct {
  164. name string
  165. args []string
  166. mutateFunc func(map[string]string) map[string]string
  167. expectedArgs []string
  168. }{
  169. {
  170. name: "normal case",
  171. args: []string{
  172. "kube-apiserver",
  173. "--admission-control=NamespaceLifecycle,LimitRanger",
  174. "--insecure-bind-address=127.0.0.1",
  175. "--allow-privileged=true",
  176. },
  177. mutateFunc: func(argMap map[string]string) map[string]string {
  178. argMap["admission-control"] = "NamespaceLifecycle,LimitRanger,ResourceQuota"
  179. return argMap
  180. },
  181. expectedArgs: []string{
  182. "kube-apiserver",
  183. "--admission-control=NamespaceLifecycle,LimitRanger,ResourceQuota",
  184. "--insecure-bind-address=127.0.0.1",
  185. "--allow-privileged=true",
  186. },
  187. },
  188. {
  189. name: "another normal case",
  190. args: []string{
  191. "kube-apiserver",
  192. "--admission-control=NamespaceLifecycle,LimitRanger",
  193. "--insecure-bind-address=127.0.0.1",
  194. "--allow-privileged=true",
  195. },
  196. mutateFunc: func(argMap map[string]string) map[string]string {
  197. argMap["new-arg-here"] = "foo"
  198. return argMap
  199. },
  200. expectedArgs: []string{
  201. "kube-apiserver",
  202. "--admission-control=NamespaceLifecycle,LimitRanger",
  203. "--insecure-bind-address=127.0.0.1",
  204. "--allow-privileged=true",
  205. "--new-arg-here=foo",
  206. },
  207. },
  208. }
  209. for _, rt := range tests {
  210. t.Run(rt.name, func(t *testing.T) {
  211. actualArgs := ReplaceArgument(rt.args, rt.mutateFunc)
  212. sort.Strings(actualArgs)
  213. sort.Strings(rt.expectedArgs)
  214. if !reflect.DeepEqual(actualArgs, rt.expectedArgs) {
  215. t.Errorf("failed ReplaceArgument:\nexpected:\n%v\nsaw:\n%v", rt.expectedArgs, actualArgs)
  216. }
  217. })
  218. }
  219. }
  220. func TestRoundtrip(t *testing.T) {
  221. var tests = []struct {
  222. name string
  223. args []string
  224. }{
  225. {
  226. name: "normal case",
  227. args: []string{
  228. "--admission-control=NamespaceLifecycle,LimitRanger",
  229. "--insecure-bind-address=127.0.0.1",
  230. "--allow-privileged=true",
  231. },
  232. },
  233. {
  234. name: "test that feature-gates is working",
  235. args: []string{
  236. "--admission-control=NamespaceLifecycle,LimitRanger",
  237. "--insecure-bind-address=127.0.0.1",
  238. "--allow-privileged=true",
  239. "--feature-gates=EnableFoo=true,EnableBar=false",
  240. },
  241. },
  242. }
  243. for _, rt := range tests {
  244. t.Run(rt.name, func(t *testing.T) {
  245. // These two methods should be each other's opposite functions, test that by chaining the methods and see if you get the same result back
  246. actual := BuildArgumentListFromMap(ParseArgumentListToMap(rt.args), map[string]string{})
  247. sort.Strings(actual)
  248. sort.Strings(rt.args)
  249. if !reflect.DeepEqual(actual, rt.args) {
  250. t.Errorf("failed TestRoundtrip:\nexpected:\n%v\nsaw:\n%v", rt.args, actual)
  251. }
  252. })
  253. }
  254. }
  255. func TestParseArgument(t *testing.T) {
  256. var tests = []struct {
  257. name string
  258. arg string
  259. expectedKey string
  260. expectedVal string
  261. expectedErr bool
  262. }{
  263. {
  264. name: "arg cannot be empty",
  265. arg: "",
  266. expectedErr: true,
  267. },
  268. {
  269. name: "arg must contain -- and =",
  270. arg: "a",
  271. expectedErr: true,
  272. },
  273. {
  274. name: "arg must contain -- and =",
  275. arg: "a-z",
  276. expectedErr: true,
  277. },
  278. {
  279. name: "arg must contain --",
  280. arg: "a=b",
  281. expectedErr: true,
  282. },
  283. {
  284. name: "arg must contain a key",
  285. arg: "--=b",
  286. expectedErr: true,
  287. },
  288. {
  289. name: "arg can contain key but no value",
  290. arg: "--a=",
  291. expectedKey: "a",
  292. expectedVal: "",
  293. expectedErr: false,
  294. },
  295. {
  296. name: "simple case",
  297. arg: "--a=b",
  298. expectedKey: "a",
  299. expectedVal: "b",
  300. expectedErr: false,
  301. },
  302. {
  303. name: "keys/values with '-' should be supported",
  304. arg: "--very-long-flag-name=some-value",
  305. expectedKey: "very-long-flag-name",
  306. expectedVal: "some-value",
  307. expectedErr: false,
  308. },
  309. {
  310. name: "numbers should be handled correctly",
  311. arg: "--some-number=0.2",
  312. expectedKey: "some-number",
  313. expectedVal: "0.2",
  314. expectedErr: false,
  315. },
  316. {
  317. name: "lists should be handled correctly",
  318. arg: "--admission-control=foo,bar,baz",
  319. expectedKey: "admission-control",
  320. expectedVal: "foo,bar,baz",
  321. expectedErr: false,
  322. },
  323. {
  324. name: "more than one '=' should be allowed",
  325. arg: "--feature-gates=EnableFoo=true,EnableBar=false",
  326. expectedKey: "feature-gates",
  327. expectedVal: "EnableFoo=true,EnableBar=false",
  328. expectedErr: false,
  329. },
  330. }
  331. for _, rt := range tests {
  332. t.Run(rt.name, func(t *testing.T) {
  333. key, val, actual := parseArgument(rt.arg)
  334. if (actual != nil) != rt.expectedErr {
  335. t.Errorf("failed parseArgument:\nexpected error:\n%t\nsaw error:\n%v", rt.expectedErr, actual)
  336. }
  337. if (key != rt.expectedKey) || (val != rt.expectedVal) {
  338. t.Errorf("failed parseArgument:\nexpected key: %s\nsaw key: %s\nexpected value: %s\nsaw value: %s", rt.expectedKey, key, rt.expectedVal, val)
  339. }
  340. })
  341. }
  342. }