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