opts_test.go 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  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. t.Run("name and alias", func(t *testing.T) {
  285. name, alias, err := ParseLink("name:alias")
  286. assert.Check(t, err)
  287. assert.Check(t, is.Equal(name, "name"))
  288. assert.Check(t, is.Equal(alias, "alias"))
  289. })
  290. t.Run("short format", func(t *testing.T) {
  291. name, alias, err := ParseLink("name")
  292. assert.Check(t, err)
  293. assert.Check(t, is.Equal(name, "name"))
  294. assert.Check(t, is.Equal(alias, "name"))
  295. })
  296. t.Run("empty string", func(t *testing.T) {
  297. _, _, err := ParseLink("")
  298. assert.Check(t, is.Error(err, "empty string specified for links"))
  299. })
  300. t.Run("more than two colons", func(t *testing.T) {
  301. _, _, err := ParseLink("link:alias:wrong")
  302. assert.Check(t, is.Error(err, "bad format for links: link:alias:wrong"))
  303. })
  304. t.Run("legacy format", func(t *testing.T) {
  305. name, alias, err := ParseLink("/foo:/c1/bar")
  306. assert.Check(t, err)
  307. assert.Check(t, is.Equal(name, "foo"))
  308. assert.Check(t, is.Equal(alias, "bar"))
  309. })
  310. }
  311. func TestMapMapOpts(t *testing.T) {
  312. tmpMap := make(map[string]map[string]string)
  313. validator := func(val string) (string, error) {
  314. if strings.HasPrefix(val, "invalid-key=") {
  315. return "", fmt.Errorf("invalid key %s", val)
  316. }
  317. return val, nil
  318. }
  319. o := NewMapMapOpts(tmpMap, validator)
  320. o.Set("r1=k11=v11")
  321. assert.Check(t, is.DeepEqual(tmpMap, map[string]map[string]string{"r1": {"k11": "v11"}}))
  322. o.Set("r2=k21=v21")
  323. assert.Check(t, is.Len(tmpMap, 2))
  324. if err := o.Set("invalid-syntax"); err == nil {
  325. t.Error("invalid mapping syntax is not being caught")
  326. }
  327. if err := o.Set("k=invalid-syntax"); err == nil {
  328. t.Error("invalid value syntax is not being caught")
  329. }
  330. o.Set("r1=k12=v12")
  331. assert.Check(t, is.DeepEqual(tmpMap["r1"], map[string]string{"k11": "v11", "k12": "v12"}))
  332. if o.Set(`invalid-key={"k":"v"}`) == nil {
  333. t.Error("validator is not being called")
  334. }
  335. }