config_test.go 12 KB

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