github.com/jwhonce/docker@v0.6.7-0.20190327063223-da823cf3a5a3/integration/secret/secret_test.go (about)

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