opts_test.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. package opts // import "github.com/docker/docker/opts"
  2. import (
  3. "fmt"
  4. "strings"
  5. "testing"
  6. "gotest.tools/v3/assert"
  7. is "gotest.tools/v3/assert/cmp"
  8. )
  9. func TestValidateIPAddress(t *testing.T) {
  10. if ret, err := ValidateIPAddress(`1.2.3.4`); err != nil || ret == "" {
  11. t.Fatalf("ValidateIPAddress(`1.2.3.4`) got %s %s", ret, err)
  12. }
  13. if ret, err := ValidateIPAddress(`127.0.0.1`); err != nil || ret == "" {
  14. t.Fatalf("ValidateIPAddress(`127.0.0.1`) got %s %s", ret, err)
  15. }
  16. if ret, err := ValidateIPAddress(`::1`); err != nil || ret == "" {
  17. t.Fatalf("ValidateIPAddress(`::1`) got %s %s", ret, err)
  18. }
  19. if ret, err := ValidateIPAddress(`127`); err == nil || ret != "" {
  20. t.Fatalf("ValidateIPAddress(`127`) got %s %s", ret, err)
  21. }
  22. if ret, err := ValidateIPAddress(`random invalid string`); err == nil || ret != "" {
  23. t.Fatalf("ValidateIPAddress(`random invalid string`) got %s %s", ret, err)
  24. }
  25. }
  26. func TestMapOpts(t *testing.T) {
  27. tmpMap := make(map[string]string)
  28. o := NewMapOpts(tmpMap, logOptsValidator)
  29. o.Set("max-size=1")
  30. if o.String() != "map[max-size:1]" {
  31. t.Errorf("%s != [map[max-size:1]", o.String())
  32. }
  33. o.Set("max-file=2")
  34. if len(tmpMap) != 2 {
  35. t.Errorf("map length %d != 2", len(tmpMap))
  36. }
  37. if tmpMap["max-file"] != "2" {
  38. t.Errorf("max-file = %s != 2", tmpMap["max-file"])
  39. }
  40. if tmpMap["max-size"] != "1" {
  41. t.Errorf("max-size = %s != 1", tmpMap["max-size"])
  42. }
  43. if o.Set("dummy-val=3") == nil {
  44. t.Error("validator is not being called")
  45. }
  46. }
  47. func TestListOptsWithoutValidator(t *testing.T) {
  48. o := NewListOpts(nil)
  49. o.Set("foo")
  50. if o.String() != "[foo]" {
  51. t.Errorf("%s != [foo]", o.String())
  52. }
  53. o.Set("bar")
  54. if o.Len() != 2 {
  55. t.Errorf("%d != 2", o.Len())
  56. }
  57. o.Set("bar")
  58. if o.Len() != 3 {
  59. t.Errorf("%d != 3", o.Len())
  60. }
  61. if !o.Get("bar") {
  62. t.Error("o.Get(\"bar\") == false")
  63. }
  64. if o.Get("baz") {
  65. t.Error("o.Get(\"baz\") == true")
  66. }
  67. o.Delete("foo")
  68. if o.String() != "[bar bar]" {
  69. t.Errorf("%s != [bar bar]", o.String())
  70. }
  71. listOpts := o.GetAll()
  72. if len(listOpts) != 2 || listOpts[0] != "bar" || listOpts[1] != "bar" {
  73. t.Errorf("Expected [[bar bar]], got [%v]", listOpts)
  74. }
  75. mapListOpts := o.GetMap()
  76. if len(mapListOpts) != 1 {
  77. t.Errorf("Expected [map[bar:{}]], got [%v]", mapListOpts)
  78. }
  79. }
  80. func TestListOptsWithValidator(t *testing.T) {
  81. // Re-using logOptsvalidator (used by MapOpts)
  82. o := NewListOpts(logOptsValidator)
  83. o.Set("foo")
  84. if o.String() != "" {
  85. t.Errorf(`%s != ""`, o.String())
  86. }
  87. o.Set("foo=bar")
  88. if o.String() != "" {
  89. t.Errorf(`%s != ""`, o.String())
  90. }
  91. o.Set("max-file=2")
  92. if o.Len() != 1 {
  93. t.Errorf("%d != 1", o.Len())
  94. }
  95. if !o.Get("max-file=2") {
  96. t.Error("o.Get(\"max-file=2\") == false")
  97. }
  98. if o.Get("baz") {
  99. t.Error("o.Get(\"baz\") == true")
  100. }
  101. o.Delete("max-file=2")
  102. if o.String() != "" {
  103. t.Errorf(`%s != ""`, o.String())
  104. }
  105. }
  106. func TestValidateDNSSearch(t *testing.T) {
  107. valid := []string{
  108. `.`,
  109. `a`,
  110. `a.`,
  111. `1.foo`,
  112. `17.foo`,
  113. `foo.bar`,
  114. `foo.bar.baz`,
  115. `foo.bar.`,
  116. `foo.bar.baz`,
  117. `foo1.bar2`,
  118. `foo1.bar2.baz`,
  119. `1foo.2bar.`,
  120. `1foo.2bar.baz`,
  121. `foo-1.bar-2`,
  122. `foo-1.bar-2.baz`,
  123. `foo-1.bar-2.`,
  124. `foo-1.bar-2.baz`,
  125. `1-foo.2-bar`,
  126. `1-foo.2-bar.baz`,
  127. `1-foo.2-bar.`,
  128. `1-foo.2-bar.baz`,
  129. }
  130. invalid := []string{
  131. ``,
  132. ` `,
  133. ` `,
  134. `17`,
  135. `17.`,
  136. `.17`,
  137. `17-.`,
  138. `17-.foo`,
  139. `.foo`,
  140. `foo-.bar`,
  141. `-foo.bar`,
  142. `foo.bar-`,
  143. `foo.bar-.baz`,
  144. `foo.-bar`,
  145. `foo.-bar.baz`,
  146. `foo.bar.baz.this.should.fail.on.long.name.because.it.is.longer.thanitshouldbethis.should.fail.on.long.name.because.it.is.longer.thanitshouldbethis.should.fail.on.long.name.because.it.is.longer.thanitshouldbethis.should.fail.on.long.name.because.it.is.longer.thanitshouldbe`,
  147. }
  148. for _, domain := range valid {
  149. if ret, err := ValidateDNSSearch(domain); err != nil || ret == "" {
  150. t.Fatalf("ValidateDNSSearch(`"+domain+"`) got %s %s", ret, err)
  151. }
  152. }
  153. for _, domain := range invalid {
  154. if ret, err := ValidateDNSSearch(domain); err == nil || ret != "" {
  155. t.Fatalf("ValidateDNSSearch(`"+domain+"`) got %s %s", ret, err)
  156. }
  157. }
  158. }
  159. func TestValidateLabel(t *testing.T) {
  160. testCases := []struct {
  161. name string
  162. label string
  163. expectedResult string
  164. expectedErr string
  165. }{
  166. {
  167. name: "lable with bad attribute format",
  168. label: "label",
  169. expectedErr: "bad attribute format: label",
  170. },
  171. {
  172. name: "label with general format",
  173. label: "key1=value1",
  174. expectedResult: "key1=value1",
  175. },
  176. {
  177. name: "label with more than one =",
  178. label: "key1=value1=value2",
  179. expectedResult: "key1=value1=value2",
  180. },
  181. {
  182. name: "label with one more",
  183. label: "key1=value1=value2=value3",
  184. expectedResult: "key1=value1=value2=value3",
  185. },
  186. {
  187. name: "label with no reserved com.docker.*",
  188. label: "com.dockerpsychnotreserved.label=value",
  189. expectedResult: "com.dockerpsychnotreserved.label=value",
  190. },
  191. {
  192. name: "label with no reserved io.docker.*",
  193. label: "io.dockerproject.not=reserved",
  194. expectedResult: "io.dockerproject.not=reserved",
  195. },
  196. {
  197. name: "label with no reserved org.dockerproject.*",
  198. label: "org.docker.not=reserved",
  199. expectedResult: "org.docker.not=reserved",
  200. },
  201. {
  202. name: "label with reserved com.docker.*",
  203. label: "com.docker.feature=enabled",
  204. expectedErr: "label com.docker.feature=enabled is not allowed: the namespaces com.docker.*, io.docker.*, and org.dockerproject.* are reserved for internal use",
  205. },
  206. {
  207. name: "label with reserved upcase com.docker.* ",
  208. label: "COM.docker.feature=enabled",
  209. expectedErr: "label COM.docker.feature=enabled is not allowed: the namespaces com.docker.*, io.docker.*, and org.dockerproject.* are reserved for internal use",
  210. },
  211. {
  212. name: "label with reserved io.docker.*",
  213. label: "io.docker.configuration=0",
  214. expectedErr: "label io.docker.configuration=0 is not allowed: the namespaces com.docker.*, io.docker.*, and org.dockerproject.* are reserved for internal use",
  215. },
  216. {
  217. name: "label with reserved upcase io.docker.*",
  218. label: "io.DOCKER.CONFIGURATion=0",
  219. expectedErr: "label io.DOCKER.CONFIGURATion=0 is not allowed: the namespaces com.docker.*, io.docker.*, and org.dockerproject.* are reserved for internal use",
  220. },
  221. {
  222. name: "label with reserved org.dockerproject.*",
  223. label: "org.dockerproject.setting=on",
  224. expectedErr: "label org.dockerproject.setting=on is not allowed: the namespaces com.docker.*, io.docker.*, and org.dockerproject.* are reserved for internal use",
  225. },
  226. {
  227. name: "label with reserved upcase org.dockerproject.*",
  228. label: "Org.Dockerproject.Setting=on",
  229. expectedErr: "label Org.Dockerproject.Setting=on is not allowed: the namespaces com.docker.*, io.docker.*, and org.dockerproject.* are reserved for internal use",
  230. },
  231. }
  232. for _, testCase := range testCases {
  233. testCase := testCase
  234. t.Run(testCase.name, func(t *testing.T) {
  235. result, err := ValidateLabel(testCase.label)
  236. if testCase.expectedErr != "" {
  237. assert.Error(t, err, testCase.expectedErr)
  238. } else {
  239. assert.NilError(t, err)
  240. }
  241. if testCase.expectedResult != "" {
  242. assert.Check(t, is.Equal(result, testCase.expectedResult))
  243. }
  244. })
  245. }
  246. }
  247. func logOptsValidator(val string) (string, error) {
  248. allowedKeys := map[string]string{"max-size": "1", "max-file": "2"}
  249. vals := strings.Split(val, "=")
  250. if allowedKeys[vals[0]] != "" {
  251. return val, nil
  252. }
  253. return "", fmt.Errorf("invalid key %s", vals[0])
  254. }
  255. func TestNamedListOpts(t *testing.T) {
  256. var v []string
  257. o := NewNamedListOptsRef("foo-name", &v, nil)
  258. o.Set("foo")
  259. if o.String() != "[foo]" {
  260. t.Errorf("%s != [foo]", o.String())
  261. }
  262. if o.Name() != "foo-name" {
  263. t.Errorf("%s != foo-name", o.Name())
  264. }
  265. if len(v) != 1 {
  266. t.Errorf("expected foo to be in the values, got %v", v)
  267. }
  268. }
  269. func TestNamedMapOpts(t *testing.T) {
  270. tmpMap := make(map[string]string)
  271. o := NewNamedMapOpts("max-name", tmpMap, nil)
  272. o.Set("max-size=1")
  273. if o.String() != "map[max-size:1]" {
  274. t.Errorf("%s != [map[max-size:1]", o.String())
  275. }
  276. if o.Name() != "max-name" {
  277. t.Errorf("%s != max-name", o.Name())
  278. }
  279. if _, exist := tmpMap["max-size"]; !exist {
  280. t.Errorf("expected map-size to be in the values, got %v", tmpMap)
  281. }
  282. }
  283. func TestParseLink(t *testing.T) {
  284. name, alias, err := ParseLink("name:alias")
  285. if err != nil {
  286. t.Fatalf("Expected not to error out on a valid name:alias format but got: %v", err)
  287. }
  288. if name != "name" {
  289. t.Fatalf("Link name should have been name, got %s instead", name)
  290. }
  291. if alias != "alias" {
  292. t.Fatalf("Link alias should have been alias, got %s instead", alias)
  293. }
  294. // short format definition
  295. name, alias, err = ParseLink("name")
  296. if err != nil {
  297. t.Fatalf("Expected not to error out on a valid name only format but got: %v", err)
  298. }
  299. if name != "name" {
  300. t.Fatalf("Link name should have been name, got %s instead", name)
  301. }
  302. if alias != "name" {
  303. t.Fatalf("Link alias should have been name, got %s instead", alias)
  304. }
  305. // empty string link definition is not allowed
  306. if _, _, err := ParseLink(""); err == nil || !strings.Contains(err.Error(), "empty string specified for links") {
  307. t.Fatalf("Expected error 'empty string specified for links' but got: %v", err)
  308. }
  309. // more than two colons are not allowed
  310. if _, _, err := ParseLink("link:alias:wrong"); err == nil || !strings.Contains(err.Error(), "bad format for links: link:alias:wrong") {
  311. t.Fatalf("Expected error 'bad format for links: link:alias:wrong' but got: %v", err)
  312. }
  313. }