github.com/rish1988/moby@v25.0.2+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  }