secret_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. package secret // import "github.com/docker/docker/integration/secret"
  2. import (
  3. "bytes"
  4. "sort"
  5. "testing"
  6. "time"
  7. "github.com/docker/docker/api/types"
  8. "github.com/docker/docker/api/types/filters"
  9. swarmtypes "github.com/docker/docker/api/types/swarm"
  10. "github.com/docker/docker/client"
  11. "github.com/docker/docker/integration/internal/swarm"
  12. "github.com/docker/docker/internal/testutil"
  13. "github.com/docker/docker/pkg/stdcopy"
  14. "github.com/gotestyourself/gotestyourself/assert"
  15. is "github.com/gotestyourself/gotestyourself/assert/cmp"
  16. "github.com/gotestyourself/gotestyourself/skip"
  17. "golang.org/x/net/context"
  18. )
  19. func TestSecretInspect(t *testing.T) {
  20. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  21. defer setupTest(t)()
  22. d := swarm.NewSwarm(t, testEnv)
  23. defer d.Stop(t)
  24. client := d.NewClientT(t)
  25. defer client.Close()
  26. ctx := context.Background()
  27. testName := "test_secret"
  28. secretID := createSecret(ctx, t, client, testName, []byte("TESTINGDATA"), nil)
  29. secret, _, err := client.SecretInspectWithRaw(context.Background(), secretID)
  30. assert.NilError(t, err)
  31. assert.Check(t, is.Equal(secret.Spec.Name, testName))
  32. secret, _, err = client.SecretInspectWithRaw(context.Background(), testName)
  33. assert.NilError(t, err)
  34. assert.Check(t, is.Equal(secretID, secretID))
  35. }
  36. func TestSecretList(t *testing.T) {
  37. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  38. defer setupTest(t)()
  39. d := swarm.NewSwarm(t, testEnv)
  40. defer d.Stop(t)
  41. client := d.NewClientT(t)
  42. defer client.Close()
  43. ctx := context.Background()
  44. testName0 := "test0"
  45. testName1 := "test1"
  46. testNames := []string{testName0, testName1}
  47. sort.Strings(testNames)
  48. // create secret test0
  49. createSecret(ctx, t, client, testName0, []byte("TESTINGDATA0"), map[string]string{"type": "test"})
  50. // create secret test1
  51. secret1ID := createSecret(ctx, t, client, testName1, []byte("TESTINGDATA1"), map[string]string{"type": "production"})
  52. names := func(entries []swarmtypes.Secret) []string {
  53. values := []string{}
  54. for _, entry := range entries {
  55. values = append(values, entry.Spec.Name)
  56. }
  57. sort.Strings(values)
  58. return values
  59. }
  60. // test by `secret ls`
  61. entries, err := client.SecretList(ctx, types.SecretListOptions{})
  62. assert.NilError(t, err)
  63. assert.Check(t, is.DeepEqual(names(entries), testNames))
  64. testCases := []struct {
  65. filters filters.Args
  66. expected []string
  67. }{
  68. // test filter by name `secret ls --filter name=xxx`
  69. {
  70. filters: filters.NewArgs(filters.Arg("name", testName0)),
  71. expected: []string{testName0},
  72. },
  73. // test filter by id `secret ls --filter id=xxx`
  74. {
  75. filters: filters.NewArgs(filters.Arg("id", secret1ID)),
  76. expected: []string{testName1},
  77. },
  78. // test filter by label `secret ls --filter label=xxx`
  79. {
  80. filters: filters.NewArgs(filters.Arg("label", "type")),
  81. expected: testNames,
  82. },
  83. {
  84. filters: filters.NewArgs(filters.Arg("label", "type=test")),
  85. expected: []string{testName0},
  86. },
  87. {
  88. filters: filters.NewArgs(filters.Arg("label", "type=production")),
  89. expected: []string{testName1},
  90. },
  91. }
  92. for _, tc := range testCases {
  93. entries, err = client.SecretList(ctx, types.SecretListOptions{
  94. Filters: tc.filters,
  95. })
  96. assert.NilError(t, err)
  97. assert.Check(t, is.DeepEqual(names(entries), tc.expected))
  98. }
  99. }
  100. func createSecret(ctx context.Context, t *testing.T, client client.APIClient, name string, data []byte, labels map[string]string) string {
  101. secret, err := client.SecretCreate(ctx, swarmtypes.SecretSpec{
  102. Annotations: swarmtypes.Annotations{
  103. Name: name,
  104. Labels: labels,
  105. },
  106. Data: data,
  107. })
  108. assert.NilError(t, err)
  109. assert.Check(t, secret.ID != "")
  110. return secret.ID
  111. }
  112. func TestSecretsCreateAndDelete(t *testing.T) {
  113. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  114. defer setupTest(t)()
  115. d := swarm.NewSwarm(t, testEnv)
  116. defer d.Stop(t)
  117. client := d.NewClientT(t)
  118. defer client.Close()
  119. ctx := context.Background()
  120. testName := "test_secret"
  121. secretID := createSecret(ctx, t, client, testName, []byte("TESTINGDATA"), nil)
  122. // create an already existin secret, daemon should return a status code of 409
  123. _, err := client.SecretCreate(ctx, swarmtypes.SecretSpec{
  124. Annotations: swarmtypes.Annotations{
  125. Name: testName,
  126. },
  127. Data: []byte("TESTINGDATA"),
  128. })
  129. testutil.ErrorContains(t, err, "already exists")
  130. // Ported from original TestSecretsDelete
  131. err = client.SecretRemove(ctx, secretID)
  132. assert.NilError(t, err)
  133. _, _, err = client.SecretInspectWithRaw(ctx, secretID)
  134. testutil.ErrorContains(t, err, "No such secret")
  135. err = client.SecretRemove(ctx, "non-existin")
  136. testutil.ErrorContains(t, err, "No such secret: non-existin")
  137. // Ported from original TestSecretsCreteaWithLabels
  138. testName = "test_secret_with_labels"
  139. secretID = createSecret(ctx, t, client, testName, []byte("TESTINGDATA"), map[string]string{
  140. "key1": "value1",
  141. "key2": "value2",
  142. })
  143. insp, _, err := client.SecretInspectWithRaw(ctx, secretID)
  144. assert.NilError(t, err)
  145. assert.Check(t, is.Equal(insp.Spec.Name, testName))
  146. assert.Check(t, is.Equal(len(insp.Spec.Labels), 2))
  147. assert.Check(t, is.Equal(insp.Spec.Labels["key1"], "value1"))
  148. assert.Check(t, is.Equal(insp.Spec.Labels["key2"], "value2"))
  149. }
  150. func TestSecretsUpdate(t *testing.T) {
  151. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  152. defer setupTest(t)()
  153. d := swarm.NewSwarm(t, testEnv)
  154. defer d.Stop(t)
  155. client := d.NewClientT(t)
  156. defer client.Close()
  157. ctx := context.Background()
  158. testName := "test_secret"
  159. secretID := createSecret(ctx, t, client, testName, []byte("TESTINGDATA"), nil)
  160. insp, _, err := client.SecretInspectWithRaw(ctx, secretID)
  161. assert.NilError(t, err)
  162. assert.Check(t, is.Equal(insp.ID, secretID))
  163. // test UpdateSecret with full ID
  164. insp.Spec.Labels = map[string]string{"test": "test1"}
  165. err = client.SecretUpdate(ctx, secretID, insp.Version, insp.Spec)
  166. assert.NilError(t, err)
  167. insp, _, err = client.SecretInspectWithRaw(ctx, secretID)
  168. assert.NilError(t, err)
  169. assert.Check(t, is.Equal(insp.Spec.Labels["test"], "test1"))
  170. // test UpdateSecret with full name
  171. insp.Spec.Labels = map[string]string{"test": "test2"}
  172. err = client.SecretUpdate(ctx, testName, insp.Version, insp.Spec)
  173. assert.NilError(t, err)
  174. insp, _, err = client.SecretInspectWithRaw(ctx, secretID)
  175. assert.NilError(t, err)
  176. assert.Check(t, is.Equal(insp.Spec.Labels["test"], "test2"))
  177. // test UpdateSecret with prefix ID
  178. insp.Spec.Labels = map[string]string{"test": "test3"}
  179. err = client.SecretUpdate(ctx, secretID[:1], insp.Version, insp.Spec)
  180. assert.NilError(t, err)
  181. insp, _, err = client.SecretInspectWithRaw(ctx, secretID)
  182. assert.NilError(t, err)
  183. assert.Check(t, is.Equal(insp.Spec.Labels["test"], "test3"))
  184. // test UpdateSecret in updating Data which is not supported in daemon
  185. // this test will produce an error in func UpdateSecret
  186. insp.Spec.Data = []byte("TESTINGDATA2")
  187. err = client.SecretUpdate(ctx, secretID, insp.Version, insp.Spec)
  188. testutil.ErrorContains(t, err, "only updates to Labels are allowed")
  189. }
  190. func TestTemplatedSecret(t *testing.T) {
  191. d := swarm.NewSwarm(t, testEnv)
  192. defer d.Stop(t)
  193. client := d.NewClientT(t)
  194. defer client.Close()
  195. ctx := context.Background()
  196. referencedSecretSpec := swarmtypes.SecretSpec{
  197. Annotations: swarmtypes.Annotations{
  198. Name: "referencedsecret",
  199. },
  200. Data: []byte("this is a secret"),
  201. }
  202. referencedSecret, err := client.SecretCreate(ctx, referencedSecretSpec)
  203. assert.Check(t, err)
  204. referencedConfigSpec := swarmtypes.ConfigSpec{
  205. Annotations: swarmtypes.Annotations{
  206. Name: "referencedconfig",
  207. },
  208. Data: []byte("this is a config"),
  209. }
  210. referencedConfig, err := client.ConfigCreate(ctx, referencedConfigSpec)
  211. assert.Check(t, err)
  212. secretSpec := swarmtypes.SecretSpec{
  213. Annotations: swarmtypes.Annotations{
  214. Name: "templated_secret",
  215. },
  216. Templating: &swarmtypes.Driver{
  217. Name: "golang",
  218. },
  219. Data: []byte("SERVICE_NAME={{.Service.Name}}\n" +
  220. "{{secret \"referencedsecrettarget\"}}\n" +
  221. "{{config \"referencedconfigtarget\"}}\n"),
  222. }
  223. templatedSecret, err := client.SecretCreate(ctx, secretSpec)
  224. assert.Check(t, err)
  225. serviceID := swarm.CreateService(t, d,
  226. swarm.ServiceWithSecret(
  227. &swarmtypes.SecretReference{
  228. File: &swarmtypes.SecretReferenceFileTarget{
  229. Name: "templated_secret",
  230. UID: "0",
  231. GID: "0",
  232. Mode: 0600,
  233. },
  234. SecretID: templatedSecret.ID,
  235. SecretName: "templated_secret",
  236. },
  237. ),
  238. swarm.ServiceWithConfig(
  239. &swarmtypes.ConfigReference{
  240. File: &swarmtypes.ConfigReferenceFileTarget{
  241. Name: "referencedconfigtarget",
  242. UID: "0",
  243. GID: "0",
  244. Mode: 0600,
  245. },
  246. ConfigID: referencedConfig.ID,
  247. ConfigName: "referencedconfig",
  248. },
  249. ),
  250. swarm.ServiceWithSecret(
  251. &swarmtypes.SecretReference{
  252. File: &swarmtypes.SecretReferenceFileTarget{
  253. Name: "referencedsecrettarget",
  254. UID: "0",
  255. GID: "0",
  256. Mode: 0600,
  257. },
  258. SecretID: referencedSecret.ID,
  259. SecretName: "referencedsecret",
  260. },
  261. ),
  262. swarm.ServiceWithName("svc"),
  263. )
  264. var tasks []swarmtypes.Task
  265. waitAndAssert(t, 60*time.Second, func(t *testing.T) bool {
  266. tasks = swarm.GetRunningTasks(t, d, serviceID)
  267. return len(tasks) > 0
  268. })
  269. task := tasks[0]
  270. waitAndAssert(t, 60*time.Second, func(t *testing.T) bool {
  271. if task.NodeID == "" || (task.Status.ContainerStatus == nil || task.Status.ContainerStatus.ContainerID == "") {
  272. task, _, _ = client.TaskInspectWithRaw(context.Background(), task.ID)
  273. }
  274. return task.NodeID != "" && task.Status.ContainerStatus != nil && task.Status.ContainerStatus.ContainerID != ""
  275. })
  276. attach := swarm.ExecTask(t, d, task, types.ExecConfig{
  277. Cmd: []string{"/bin/cat", "/run/secrets/templated_secret"},
  278. AttachStdout: true,
  279. AttachStderr: true,
  280. })
  281. expect := "SERVICE_NAME=svc\n" +
  282. "this is a secret\n" +
  283. "this is a config\n"
  284. assertAttachedStream(t, attach, expect)
  285. attach = swarm.ExecTask(t, d, task, types.ExecConfig{
  286. Cmd: []string{"mount"},
  287. AttachStdout: true,
  288. AttachStderr: true,
  289. })
  290. assertAttachedStream(t, attach, "tmpfs on /run/secrets/templated_secret type tmpfs")
  291. }
  292. func assertAttachedStream(t *testing.T, attach types.HijackedResponse, expect string) {
  293. buf := bytes.NewBuffer(nil)
  294. _, err := stdcopy.StdCopy(buf, buf, attach.Reader)
  295. assert.NilError(t, err)
  296. assert.Check(t, is.Contains(buf.String(), expect))
  297. }
  298. func waitAndAssert(t *testing.T, timeout time.Duration, f func(*testing.T) bool) {
  299. t.Helper()
  300. after := time.After(timeout)
  301. for {
  302. select {
  303. case <-after:
  304. t.Fatalf("timed out waiting for condition")
  305. default:
  306. }
  307. if f(t) {
  308. return
  309. }
  310. time.Sleep(100 * time.Millisecond)
  311. }
  312. }