config_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. package config // import "github.com/docker/docker/integration/config"
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/json"
  6. "io/ioutil"
  7. "path/filepath"
  8. "sort"
  9. "testing"
  10. "time"
  11. "github.com/docker/docker/api/types"
  12. "github.com/docker/docker/api/types/filters"
  13. swarmtypes "github.com/docker/docker/api/types/swarm"
  14. "github.com/docker/docker/client"
  15. "github.com/docker/docker/integration/internal/swarm"
  16. "github.com/docker/docker/internal/test/daemon"
  17. "github.com/docker/docker/pkg/stdcopy"
  18. "gotest.tools/assert"
  19. is "gotest.tools/assert/cmp"
  20. "gotest.tools/poll"
  21. "gotest.tools/skip"
  22. )
  23. func TestConfigList(t *testing.T) {
  24. skip.If(t, testEnv.DaemonInfo.OSType == "windows")
  25. defer setupTest(t)()
  26. d := swarm.NewSwarm(t, testEnv)
  27. defer d.Stop(t)
  28. client := d.NewClientT(t)
  29. defer client.Close()
  30. ctx := context.Background()
  31. // This test case is ported from the original TestConfigsEmptyList
  32. configs, err := client.ConfigList(ctx, types.ConfigListOptions{})
  33. assert.NilError(t, err)
  34. assert.Check(t, is.Equal(len(configs), 0))
  35. testName0 := "test0-" + t.Name()
  36. testName1 := "test1-" + t.Name()
  37. testNames := []string{testName0, testName1}
  38. sort.Strings(testNames)
  39. // create config test0
  40. createConfig(ctx, t, client, testName0, []byte("TESTINGDATA0"), map[string]string{"type": "test"})
  41. config1ID := createConfig(ctx, t, client, testName1, []byte("TESTINGDATA1"), map[string]string{"type": "production"})
  42. // test by `config ls`
  43. entries, err := client.ConfigList(ctx, types.ConfigListOptions{})
  44. assert.NilError(t, err)
  45. assert.Check(t, is.DeepEqual(configNamesFromList(entries), testNames))
  46. testCases := []struct {
  47. filters filters.Args
  48. expected []string
  49. }{
  50. // test filter by name `config ls --filter name=xxx`
  51. {
  52. filters: filters.NewArgs(filters.Arg("name", testName0)),
  53. expected: []string{testName0},
  54. },
  55. // test filter by id `config ls --filter id=xxx`
  56. {
  57. filters: filters.NewArgs(filters.Arg("id", config1ID)),
  58. expected: []string{testName1},
  59. },
  60. // test filter by label `config ls --filter label=xxx`
  61. {
  62. filters: filters.NewArgs(filters.Arg("label", "type")),
  63. expected: testNames,
  64. },
  65. {
  66. filters: filters.NewArgs(filters.Arg("label", "type=test")),
  67. expected: []string{testName0},
  68. },
  69. {
  70. filters: filters.NewArgs(filters.Arg("label", "type=production")),
  71. expected: []string{testName1},
  72. },
  73. }
  74. for _, tc := range testCases {
  75. entries, err = client.ConfigList(ctx, types.ConfigListOptions{
  76. Filters: tc.filters,
  77. })
  78. assert.NilError(t, err)
  79. assert.Check(t, is.DeepEqual(configNamesFromList(entries), tc.expected))
  80. }
  81. }
  82. func createConfig(ctx context.Context, t *testing.T, client client.APIClient, name string, data []byte, labels map[string]string) string {
  83. config, err := client.ConfigCreate(ctx, swarmtypes.ConfigSpec{
  84. Annotations: swarmtypes.Annotations{
  85. Name: name,
  86. Labels: labels,
  87. },
  88. Data: data,
  89. })
  90. assert.NilError(t, err)
  91. assert.Check(t, config.ID != "")
  92. return config.ID
  93. }
  94. func TestConfigsCreateAndDelete(t *testing.T) {
  95. skip.If(t, testEnv.DaemonInfo.OSType == "windows")
  96. defer setupTest(t)()
  97. d := swarm.NewSwarm(t, testEnv)
  98. defer d.Stop(t)
  99. client := d.NewClientT(t)
  100. defer client.Close()
  101. ctx := context.Background()
  102. testName := "test_config-" + t.Name()
  103. // This test case is ported from the original TestConfigsCreate
  104. configID := createConfig(ctx, t, client, testName, []byte("TESTINGDATA"), nil)
  105. insp, _, err := client.ConfigInspectWithRaw(ctx, configID)
  106. assert.NilError(t, err)
  107. assert.Check(t, is.Equal(insp.Spec.Name, testName))
  108. // This test case is ported from the original TestConfigsDelete
  109. err = client.ConfigRemove(ctx, configID)
  110. assert.NilError(t, err)
  111. insp, _, err = client.ConfigInspectWithRaw(ctx, configID)
  112. assert.Check(t, is.ErrorContains(err, "No such config"))
  113. }
  114. func TestConfigsUpdate(t *testing.T) {
  115. skip.If(t, testEnv.DaemonInfo.OSType == "windows")
  116. defer setupTest(t)()
  117. d := swarm.NewSwarm(t, testEnv)
  118. defer d.Stop(t)
  119. client := d.NewClientT(t)
  120. defer client.Close()
  121. ctx := context.Background()
  122. testName := "test_config-" + t.Name()
  123. // This test case is ported from the original TestConfigsCreate
  124. configID := createConfig(ctx, t, client, testName, []byte("TESTINGDATA"), nil)
  125. insp, _, err := client.ConfigInspectWithRaw(ctx, configID)
  126. assert.NilError(t, err)
  127. assert.Check(t, is.Equal(insp.ID, configID))
  128. // test UpdateConfig with full ID
  129. insp.Spec.Labels = map[string]string{"test": "test1"}
  130. err = client.ConfigUpdate(ctx, configID, insp.Version, insp.Spec)
  131. assert.NilError(t, err)
  132. insp, _, err = client.ConfigInspectWithRaw(ctx, configID)
  133. assert.NilError(t, err)
  134. assert.Check(t, is.Equal(insp.Spec.Labels["test"], "test1"))
  135. // test UpdateConfig with full name
  136. insp.Spec.Labels = map[string]string{"test": "test2"}
  137. err = client.ConfigUpdate(ctx, testName, insp.Version, insp.Spec)
  138. assert.NilError(t, err)
  139. insp, _, err = client.ConfigInspectWithRaw(ctx, configID)
  140. assert.NilError(t, err)
  141. assert.Check(t, is.Equal(insp.Spec.Labels["test"], "test2"))
  142. // test UpdateConfig with prefix ID
  143. insp.Spec.Labels = map[string]string{"test": "test3"}
  144. err = client.ConfigUpdate(ctx, configID[:1], insp.Version, insp.Spec)
  145. assert.NilError(t, err)
  146. insp, _, err = client.ConfigInspectWithRaw(ctx, configID)
  147. assert.NilError(t, err)
  148. assert.Check(t, is.Equal(insp.Spec.Labels["test"], "test3"))
  149. // test UpdateConfig in updating Data which is not supported in daemon
  150. // this test will produce an error in func UpdateConfig
  151. insp.Spec.Data = []byte("TESTINGDATA2")
  152. err = client.ConfigUpdate(ctx, configID, insp.Version, insp.Spec)
  153. assert.Check(t, is.ErrorContains(err, "only updates to Labels are allowed"))
  154. }
  155. func TestTemplatedConfig(t *testing.T) {
  156. skip.If(t, testEnv.DaemonInfo.OSType == "windows")
  157. d := swarm.NewSwarm(t, testEnv)
  158. defer d.Stop(t)
  159. client := d.NewClientT(t)
  160. defer client.Close()
  161. ctx := context.Background()
  162. referencedSecretName := "referencedsecret-" + t.Name()
  163. referencedSecretSpec := swarmtypes.SecretSpec{
  164. Annotations: swarmtypes.Annotations{
  165. Name: referencedSecretName,
  166. },
  167. Data: []byte("this is a secret"),
  168. }
  169. referencedSecret, err := client.SecretCreate(ctx, referencedSecretSpec)
  170. assert.Check(t, err)
  171. referencedConfigName := "referencedconfig-" + t.Name()
  172. referencedConfigSpec := swarmtypes.ConfigSpec{
  173. Annotations: swarmtypes.Annotations{
  174. Name: referencedConfigName,
  175. },
  176. Data: []byte("this is a config"),
  177. }
  178. referencedConfig, err := client.ConfigCreate(ctx, referencedConfigSpec)
  179. assert.Check(t, err)
  180. templatedConfigName := "templated_config-" + t.Name()
  181. configSpec := swarmtypes.ConfigSpec{
  182. Annotations: swarmtypes.Annotations{
  183. Name: templatedConfigName,
  184. },
  185. Templating: &swarmtypes.Driver{
  186. Name: "golang",
  187. },
  188. Data: []byte("SERVICE_NAME={{.Service.Name}}\n" +
  189. "{{secret \"referencedsecrettarget\"}}\n" +
  190. "{{config \"referencedconfigtarget\"}}\n"),
  191. }
  192. templatedConfig, err := client.ConfigCreate(ctx, configSpec)
  193. assert.Check(t, err)
  194. serviceID := swarm.CreateService(t, d,
  195. swarm.ServiceWithConfig(
  196. &swarmtypes.ConfigReference{
  197. File: &swarmtypes.ConfigReferenceFileTarget{
  198. Name: "/" + templatedConfigName,
  199. UID: "0",
  200. GID: "0",
  201. Mode: 0600,
  202. },
  203. ConfigID: templatedConfig.ID,
  204. ConfigName: templatedConfigName,
  205. },
  206. ),
  207. swarm.ServiceWithConfig(
  208. &swarmtypes.ConfigReference{
  209. File: &swarmtypes.ConfigReferenceFileTarget{
  210. Name: "referencedconfigtarget",
  211. UID: "0",
  212. GID: "0",
  213. Mode: 0600,
  214. },
  215. ConfigID: referencedConfig.ID,
  216. ConfigName: referencedConfigName,
  217. },
  218. ),
  219. swarm.ServiceWithSecret(
  220. &swarmtypes.SecretReference{
  221. File: &swarmtypes.SecretReferenceFileTarget{
  222. Name: "referencedsecrettarget",
  223. UID: "0",
  224. GID: "0",
  225. Mode: 0600,
  226. },
  227. SecretID: referencedSecret.ID,
  228. SecretName: referencedSecretName,
  229. },
  230. ),
  231. swarm.ServiceWithName("svc"),
  232. )
  233. var tasks []swarmtypes.Task
  234. getRunningTasks := func(log poll.LogT) poll.Result {
  235. tasks = swarm.GetRunningTasks(t, client, serviceID)
  236. if len(tasks) > 0 {
  237. return poll.Success()
  238. }
  239. return poll.Continue("task still waiting")
  240. }
  241. poll.WaitOn(t, getRunningTasks, swarm.ServicePoll, poll.WithTimeout(1*time.Minute))
  242. task := tasks[0]
  243. getTask := func(log poll.LogT) poll.Result {
  244. if task.NodeID == "" || (task.Status.ContainerStatus == nil || task.Status.ContainerStatus.ContainerID == "") {
  245. task, _, _ = client.TaskInspectWithRaw(context.Background(), task.ID)
  246. }
  247. if task.NodeID != "" && task.Status.ContainerStatus != nil && task.Status.ContainerStatus.ContainerID != "" {
  248. return poll.Success()
  249. }
  250. return poll.Continue("task still waiting")
  251. }
  252. poll.WaitOn(t, getTask, swarm.ServicePoll, poll.WithTimeout(1*time.Minute))
  253. attach := swarm.ExecTask(t, d, task, types.ExecConfig{
  254. Cmd: []string{"/bin/cat", "/" + templatedConfigName},
  255. AttachStdout: true,
  256. AttachStderr: true,
  257. })
  258. expect := "SERVICE_NAME=svc\n" +
  259. "this is a secret\n" +
  260. "this is a config\n"
  261. assertAttachedStream(t, attach, expect)
  262. attach = swarm.ExecTask(t, d, task, types.ExecConfig{
  263. Cmd: []string{"mount"},
  264. AttachStdout: true,
  265. AttachStderr: true,
  266. })
  267. assertAttachedStream(t, attach, "tmpfs on /"+templatedConfigName+" type tmpfs")
  268. }
  269. func assertAttachedStream(t *testing.T, attach types.HijackedResponse, expect string) {
  270. buf := bytes.NewBuffer(nil)
  271. _, err := stdcopy.StdCopy(buf, buf, attach.Reader)
  272. assert.NilError(t, err)
  273. assert.Check(t, is.Contains(buf.String(), expect))
  274. }
  275. func TestConfigInspect(t *testing.T) {
  276. skip.If(t, testEnv.DaemonInfo.OSType == "windows")
  277. defer setupTest(t)()
  278. d := swarm.NewSwarm(t, testEnv)
  279. defer d.Stop(t)
  280. client := d.NewClientT(t)
  281. defer client.Close()
  282. ctx := context.Background()
  283. testName := t.Name()
  284. configID := createConfig(ctx, t, client, testName, []byte("TESTINGDATA"), nil)
  285. insp, body, err := client.ConfigInspectWithRaw(ctx, configID)
  286. assert.NilError(t, err)
  287. assert.Check(t, is.Equal(insp.Spec.Name, testName))
  288. var config swarmtypes.Config
  289. err = json.Unmarshal(body, &config)
  290. assert.NilError(t, err)
  291. assert.Check(t, is.DeepEqual(config, insp))
  292. }
  293. func TestConfigCreateWithLabels(t *testing.T) {
  294. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  295. defer setupTest(t)()
  296. d := swarm.NewSwarm(t, testEnv)
  297. defer d.Stop(t)
  298. client := d.NewClientT(t)
  299. defer client.Close()
  300. ctx := context.Background()
  301. labels := map[string]string{
  302. "key1": "value1",
  303. "key2": "value2",
  304. }
  305. testName := t.Name()
  306. configID := createConfig(ctx, t, client, testName, []byte("TESTINGDATA"), labels)
  307. insp, _, err := client.ConfigInspectWithRaw(ctx, configID)
  308. assert.NilError(t, err)
  309. assert.Check(t, is.Equal(insp.Spec.Name, testName))
  310. assert.Check(t, is.Equal(2, len(insp.Spec.Labels)))
  311. assert.Check(t, is.Equal("value1", insp.Spec.Labels["key1"]))
  312. assert.Check(t, is.Equal("value2", insp.Spec.Labels["key2"]))
  313. }
  314. // Test case for 28884
  315. func TestConfigCreateResolve(t *testing.T) {
  316. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  317. defer setupTest(t)()
  318. d := swarm.NewSwarm(t, testEnv)
  319. defer d.Stop(t)
  320. client := d.NewClientT(t)
  321. defer client.Close()
  322. ctx := context.Background()
  323. configName := "test_config_" + t.Name()
  324. configID := createConfig(ctx, t, client, configName, []byte("foo"), nil)
  325. fakeName := configID
  326. fakeID := createConfig(ctx, t, client, fakeName, []byte("fake foo"), nil)
  327. entries, err := client.ConfigList(ctx, types.ConfigListOptions{})
  328. assert.NilError(t, err)
  329. assert.Assert(t, is.Contains(configNamesFromList(entries), configName))
  330. assert.Assert(t, is.Contains(configNamesFromList(entries), fakeName))
  331. err = client.ConfigRemove(ctx, configID)
  332. assert.NilError(t, err)
  333. // Fake one will remain
  334. entries, err = client.ConfigList(ctx, types.ConfigListOptions{})
  335. assert.NilError(t, err)
  336. assert.Assert(t, is.DeepEqual(configNamesFromList(entries), []string{fakeName}))
  337. // Remove based on name prefix of the fake one
  338. // (which is the same as the ID of foo one) should not work
  339. // as search is only done based on:
  340. // - Full ID
  341. // - Full Name
  342. // - Partial ID (prefix)
  343. err = client.ConfigRemove(ctx, configID[:5])
  344. assert.Assert(t, nil != err)
  345. entries, err = client.ConfigList(ctx, types.ConfigListOptions{})
  346. assert.NilError(t, err)
  347. assert.Assert(t, is.DeepEqual(configNamesFromList(entries), []string{fakeName}))
  348. // Remove based on ID prefix of the fake one should succeed
  349. err = client.ConfigRemove(ctx, fakeID[:5])
  350. assert.NilError(t, err)
  351. entries, err = client.ConfigList(ctx, types.ConfigListOptions{})
  352. assert.NilError(t, err)
  353. assert.Assert(t, is.Equal(0, len(entries)))
  354. }
  355. func TestConfigDaemonLibtrustID(t *testing.T) {
  356. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  357. defer setupTest(t)()
  358. d := daemon.New(t)
  359. defer d.Stop(t)
  360. trustKey := filepath.Join(d.RootDir(), "key.json")
  361. err := ioutil.WriteFile(trustKey, []byte(`{"crv":"P-256","d":"dm28PH4Z4EbyUN8L0bPonAciAQa1QJmmyYd876mnypY","kid":"WTJ3:YSIP:CE2E:G6KJ:PSBD:YX2Y:WEYD:M64G:NU2V:XPZV:H2CR:VLUB","kty":"EC","x":"Mh5-JINSjaa_EZdXDttri255Z5fbCEOTQIZjAcScFTk","y":"eUyuAjfxevb07hCCpvi4Zi334Dy4GDWQvEToGEX4exQ"}`), 0644)
  362. assert.NilError(t, err)
  363. config := filepath.Join(d.RootDir(), "daemon.json")
  364. err = ioutil.WriteFile(config, []byte(`{"deprecated-key-path": "`+trustKey+`"}`), 0644)
  365. assert.NilError(t, err)
  366. d.Start(t, "--config-file", config)
  367. info := d.Info(t)
  368. assert.Equal(t, info.ID, "WTJ3:YSIP:CE2E:G6KJ:PSBD:YX2Y:WEYD:M64G:NU2V:XPZV:H2CR:VLUB")
  369. }
  370. func configNamesFromList(entries []swarmtypes.Config) []string {
  371. var values []string
  372. for _, entry := range entries {
  373. values = append(values, entry.Spec.Name)
  374. }
  375. sort.Strings(values)
  376. return values
  377. }