secret_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. package secret // import "github.com/docker/docker/integration/secret"
  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 TestSecretInspect(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. secretID := createSecret(ctx, t, c, testName, []byte("TESTINGDATA"), nil)
  31. insp, body, err := c.SecretInspectWithRaw(ctx, secretID)
  32. assert.NilError(t, err)
  33. assert.Check(t, is.Equal(insp.Spec.Name, testName))
  34. var secret swarmtypes.Secret
  35. err = json.Unmarshal(body, &secret)
  36. assert.NilError(t, err)
  37. assert.Check(t, is.DeepEqual(secret, insp))
  38. }
  39. func TestSecretList(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. configs, err := c.SecretList(ctx, types.SecretListOptions{})
  47. assert.NilError(t, err)
  48. assert.Check(t, is.Equal(len(configs), 0))
  49. testName0 := "test0_" + t.Name()
  50. testName1 := "test1_" + t.Name()
  51. testNames := []string{testName0, testName1}
  52. sort.Strings(testNames)
  53. // create secret test0
  54. createSecret(ctx, t, c, testName0, []byte("TESTINGDATA0"), map[string]string{"type": "test"})
  55. // create secret test1
  56. secret1ID := createSecret(ctx, t, c, testName1, []byte("TESTINGDATA1"), map[string]string{"type": "production"})
  57. // test by `secret ls`
  58. entries, err := c.SecretList(ctx, types.SecretListOptions{})
  59. assert.NilError(t, err)
  60. assert.Check(t, is.DeepEqual(secretNamesFromList(entries), testNames))
  61. testCases := []struct {
  62. filters filters.Args
  63. expected []string
  64. }{
  65. // test filter by name `secret ls --filter name=xxx`
  66. {
  67. filters: filters.NewArgs(filters.Arg("name", testName0)),
  68. expected: []string{testName0},
  69. },
  70. // test filter by id `secret ls --filter id=xxx`
  71. {
  72. filters: filters.NewArgs(filters.Arg("id", secret1ID)),
  73. expected: []string{testName1},
  74. },
  75. // test filter by label `secret ls --filter label=xxx`
  76. {
  77. filters: filters.NewArgs(filters.Arg("label", "type")),
  78. expected: testNames,
  79. },
  80. {
  81. filters: filters.NewArgs(filters.Arg("label", "type=test")),
  82. expected: []string{testName0},
  83. },
  84. {
  85. filters: filters.NewArgs(filters.Arg("label", "type=production")),
  86. expected: []string{testName1},
  87. },
  88. }
  89. for _, tc := range testCases {
  90. entries, err = c.SecretList(ctx, types.SecretListOptions{
  91. Filters: tc.filters,
  92. })
  93. assert.NilError(t, err)
  94. assert.Check(t, is.DeepEqual(secretNamesFromList(entries), tc.expected))
  95. }
  96. }
  97. func createSecret(ctx context.Context, t *testing.T, client client.APIClient, name string, data []byte, labels map[string]string) string {
  98. secret, err := client.SecretCreate(ctx, swarmtypes.SecretSpec{
  99. Annotations: swarmtypes.Annotations{
  100. Name: name,
  101. Labels: labels,
  102. },
  103. Data: data,
  104. })
  105. assert.NilError(t, err)
  106. assert.Check(t, secret.ID != "")
  107. return secret.ID
  108. }
  109. func TestSecretsCreateAndDelete(t *testing.T) {
  110. skip.If(t, testEnv.DaemonInfo.OSType == "windows")
  111. ctx := setupTest(t)
  112. d := swarm.NewSwarm(ctx, t, testEnv)
  113. defer d.Stop(t)
  114. c := d.NewClientT(t)
  115. defer c.Close()
  116. testName := "test_secret_" + t.Name()
  117. secretID := createSecret(ctx, t, c, testName, []byte("TESTINGDATA"), nil)
  118. // create an already existing secret, daemon should return a status code of 409
  119. _, err := c.SecretCreate(ctx, swarmtypes.SecretSpec{
  120. Annotations: swarmtypes.Annotations{
  121. Name: testName,
  122. },
  123. Data: []byte("TESTINGDATA"),
  124. })
  125. assert.Check(t, errdefs.IsConflict(err))
  126. assert.Check(t, is.ErrorContains(err, testName))
  127. err = c.SecretRemove(ctx, secretID)
  128. assert.NilError(t, err)
  129. _, _, err = c.SecretInspectWithRaw(ctx, secretID)
  130. assert.Check(t, errdefs.IsNotFound(err))
  131. assert.Check(t, is.ErrorContains(err, secretID))
  132. err = c.SecretRemove(ctx, "non-existing")
  133. assert.Check(t, errdefs.IsNotFound(err))
  134. assert.Check(t, is.ErrorContains(err, "non-existing"))
  135. testName = "test_secret_with_labels_" + t.Name()
  136. secretID = createSecret(ctx, t, c, testName, []byte("TESTINGDATA"), map[string]string{
  137. "key1": "value1",
  138. "key2": "value2",
  139. })
  140. insp, _, err := c.SecretInspectWithRaw(ctx, secretID)
  141. assert.NilError(t, err)
  142. assert.Check(t, is.Equal(insp.Spec.Name, testName))
  143. assert.Check(t, is.Equal(len(insp.Spec.Labels), 2))
  144. assert.Check(t, is.Equal(insp.Spec.Labels["key1"], "value1"))
  145. assert.Check(t, is.Equal(insp.Spec.Labels["key2"], "value2"))
  146. }
  147. func TestSecretsUpdate(t *testing.T) {
  148. skip.If(t, testEnv.DaemonInfo.OSType == "windows")
  149. ctx := setupTest(t)
  150. d := swarm.NewSwarm(ctx, t, testEnv)
  151. defer d.Stop(t)
  152. c := d.NewClientT(t)
  153. defer c.Close()
  154. testName := "test_secret_" + t.Name()
  155. secretID := createSecret(ctx, t, c, testName, []byte("TESTINGDATA"), nil)
  156. insp, _, err := c.SecretInspectWithRaw(ctx, secretID)
  157. assert.NilError(t, err)
  158. assert.Check(t, is.Equal(insp.ID, secretID))
  159. // test UpdateSecret with full ID
  160. insp.Spec.Labels = map[string]string{"test": "test1"}
  161. err = c.SecretUpdate(ctx, secretID, insp.Version, insp.Spec)
  162. assert.NilError(t, err)
  163. insp, _, err = c.SecretInspectWithRaw(ctx, secretID)
  164. assert.NilError(t, err)
  165. assert.Check(t, is.Equal(insp.Spec.Labels["test"], "test1"))
  166. // test UpdateSecret with full name
  167. insp.Spec.Labels = map[string]string{"test": "test2"}
  168. err = c.SecretUpdate(ctx, testName, insp.Version, insp.Spec)
  169. assert.NilError(t, err)
  170. insp, _, err = c.SecretInspectWithRaw(ctx, secretID)
  171. assert.NilError(t, err)
  172. assert.Check(t, is.Equal(insp.Spec.Labels["test"], "test2"))
  173. // test UpdateSecret with prefix ID
  174. insp.Spec.Labels = map[string]string{"test": "test3"}
  175. err = c.SecretUpdate(ctx, secretID[:1], insp.Version, insp.Spec)
  176. assert.NilError(t, err)
  177. insp, _, err = c.SecretInspectWithRaw(ctx, secretID)
  178. assert.NilError(t, err)
  179. assert.Check(t, is.Equal(insp.Spec.Labels["test"], "test3"))
  180. // test UpdateSecret in updating Data which is not supported in daemon
  181. // this test will produce an error in func UpdateSecret
  182. insp.Spec.Data = []byte("TESTINGDATA2")
  183. err = c.SecretUpdate(ctx, secretID, insp.Version, insp.Spec)
  184. assert.Check(t, errdefs.IsInvalidParameter(err))
  185. assert.Check(t, is.ErrorContains(err, "only updates to Labels are allowed"))
  186. }
  187. func TestTemplatedSecret(t *testing.T) {
  188. skip.If(t, testEnv.DaemonInfo.OSType == "windows")
  189. ctx := testutil.StartSpan(baseContext, t)
  190. d := swarm.NewSwarm(ctx, t, testEnv)
  191. defer d.Stop(t)
  192. c := d.NewClientT(t)
  193. defer c.Close()
  194. referencedSecretName := "referencedsecret_" + t.Name()
  195. referencedSecretSpec := swarmtypes.SecretSpec{
  196. Annotations: swarmtypes.Annotations{
  197. Name: referencedSecretName,
  198. },
  199. Data: []byte("this is a secret"),
  200. }
  201. referencedSecret, err := c.SecretCreate(ctx, referencedSecretSpec)
  202. assert.Check(t, err)
  203. referencedConfigName := "referencedconfig_" + t.Name()
  204. referencedConfigSpec := swarmtypes.ConfigSpec{
  205. Annotations: swarmtypes.Annotations{
  206. Name: referencedConfigName,
  207. },
  208. Data: []byte("this is a config"),
  209. }
  210. referencedConfig, err := c.ConfigCreate(ctx, referencedConfigSpec)
  211. assert.Check(t, err)
  212. templatedSecretName := "templated_secret_" + t.Name()
  213. secretSpec := swarmtypes.SecretSpec{
  214. Annotations: swarmtypes.Annotations{
  215. Name: templatedSecretName,
  216. },
  217. Templating: &swarmtypes.Driver{
  218. Name: "golang",
  219. },
  220. Data: []byte("SERVICE_NAME={{.Service.Name}}\n" +
  221. "{{secret \"referencedsecrettarget\"}}\n" +
  222. "{{config \"referencedconfigtarget\"}}\n"),
  223. }
  224. templatedSecret, err := c.SecretCreate(ctx, secretSpec)
  225. assert.Check(t, err)
  226. serviceName := "svc_" + t.Name()
  227. serviceID := swarm.CreateService(ctx, t, d,
  228. swarm.ServiceWithSecret(
  229. &swarmtypes.SecretReference{
  230. File: &swarmtypes.SecretReferenceFileTarget{
  231. Name: "templated_secret",
  232. UID: "0",
  233. GID: "0",
  234. Mode: 0o600,
  235. },
  236. SecretID: templatedSecret.ID,
  237. SecretName: templatedSecretName,
  238. },
  239. ),
  240. swarm.ServiceWithConfig(
  241. &swarmtypes.ConfigReference{
  242. File: &swarmtypes.ConfigReferenceFileTarget{
  243. Name: "referencedconfigtarget",
  244. UID: "0",
  245. GID: "0",
  246. Mode: 0o600,
  247. },
  248. ConfigID: referencedConfig.ID,
  249. ConfigName: referencedConfigName,
  250. },
  251. ),
  252. swarm.ServiceWithSecret(
  253. &swarmtypes.SecretReference{
  254. File: &swarmtypes.SecretReferenceFileTarget{
  255. Name: "referencedsecrettarget",
  256. UID: "0",
  257. GID: "0",
  258. Mode: 0o600,
  259. },
  260. SecretID: referencedSecret.ID,
  261. SecretName: referencedSecretName,
  262. },
  263. ),
  264. swarm.ServiceWithName(serviceName),
  265. )
  266. poll.WaitOn(t, swarm.RunningTasksCount(ctx, c, serviceID, 1), swarm.ServicePoll, poll.WithTimeout(1*time.Minute))
  267. tasks := swarm.GetRunningTasks(ctx, t, c, serviceID)
  268. assert.Assert(t, len(tasks) > 0, "no running tasks found for service %s", serviceID)
  269. attach := swarm.ExecTask(ctx, t, d, tasks[0], types.ExecConfig{
  270. Cmd: []string{"/bin/cat", "/run/secrets/templated_secret"},
  271. AttachStdout: true,
  272. AttachStderr: true,
  273. })
  274. expect := "SERVICE_NAME=" + serviceName + "\n" +
  275. "this is a secret\n" +
  276. "this is a config\n"
  277. assertAttachedStream(t, attach, expect)
  278. attach = swarm.ExecTask(ctx, t, d, tasks[0], types.ExecConfig{
  279. Cmd: []string{"mount"},
  280. AttachStdout: true,
  281. AttachStderr: true,
  282. })
  283. assertAttachedStream(t, attach, "tmpfs on /run/secrets/templated_secret type tmpfs")
  284. }
  285. // Test case for 28884
  286. func TestSecretCreateResolve(t *testing.T) {
  287. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  288. ctx := setupTest(t)
  289. d := swarm.NewSwarm(ctx, t, testEnv)
  290. defer d.Stop(t)
  291. c := d.NewClientT(t)
  292. defer c.Close()
  293. testName := "test_secret_" + t.Name()
  294. secretID := createSecret(ctx, t, c, testName, []byte("foo"), nil)
  295. fakeName := secretID
  296. fakeID := createSecret(ctx, t, c, fakeName, []byte("fake foo"), nil)
  297. entries, err := c.SecretList(ctx, types.SecretListOptions{})
  298. assert.NilError(t, err)
  299. assert.Check(t, is.Contains(secretNamesFromList(entries), testName))
  300. assert.Check(t, is.Contains(secretNamesFromList(entries), fakeName))
  301. err = c.SecretRemove(ctx, secretID)
  302. assert.NilError(t, err)
  303. // Fake one will remain
  304. entries, err = c.SecretList(ctx, types.SecretListOptions{})
  305. assert.NilError(t, err)
  306. assert.Assert(t, is.DeepEqual(secretNamesFromList(entries), []string{fakeName}))
  307. // Remove based on name prefix of the fake 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.SecretRemove(ctx, fakeName[:5])
  313. assert.Assert(t, nil != err)
  314. entries, err = c.SecretList(ctx, types.SecretListOptions{})
  315. assert.NilError(t, err)
  316. assert.Assert(t, is.DeepEqual(secretNamesFromList(entries), []string{fakeName}))
  317. // Remove based on ID prefix of the fake one should succeed
  318. err = c.SecretRemove(ctx, fakeID[:5])
  319. assert.NilError(t, err)
  320. entries, err = c.SecretList(ctx, types.SecretListOptions{})
  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 secretNamesFromList(entries []swarmtypes.Secret) []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. }