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