config_test.go 12 KB

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