Browse code

Add SecretUpdate method to client

closes #28678

Docker-DCO-1.1-Signed-off-by: Josh Hawn <josh.hawn@docker.com> (github: jlhawn)

Update cli/command/service/update_test.go

Fixes test build error:

secretAPIClientMock does not implement "github.com/docker/docker/client".SecretAPIClient (missing SecretUpdate method)

Docker-DCO-1.1-Signed-off-by: Josh Hawn <josh.hawn@docker.com> (github: jlhawn)

(cherry picked from commit 77b8465d7e68ca102d7aae839c7b3fe0ecd28398)
Signed-off-by: Victor Vieux <vieux@docker.com>

Josh Hawn authored on 2016/11/23 04:03:23
Showing 4 changed files
... ...
@@ -1,11 +1,13 @@
1 1
 package service
2 2
 
3 3
 import (
4
+	"context"
4 5
 	"reflect"
5 6
 	"sort"
6 7
 	"testing"
7 8
 	"time"
8 9
 
10
+	"github.com/docker/docker/api/types"
9 11
 	"github.com/docker/docker/api/types/container"
10 12
 	mounttypes "github.com/docker/docker/api/types/mount"
11 13
 	"github.com/docker/docker/api/types/swarm"
... ...
@@ -382,3 +384,61 @@ func TestValidatePort(t *testing.T) {
382 382
 		assert.Error(t, err, e)
383 383
 	}
384 384
 }
385
+
386
+type secretAPIClientMock struct {
387
+	listResult []swarm.Secret
388
+}
389
+
390
+func (s secretAPIClientMock) SecretList(ctx context.Context, options types.SecretListOptions) ([]swarm.Secret, error) {
391
+	return s.listResult, nil
392
+}
393
+func (s secretAPIClientMock) SecretCreate(ctx context.Context, secret swarm.SecretSpec) (types.SecretCreateResponse, error) {
394
+	return types.SecretCreateResponse{}, nil
395
+}
396
+func (s secretAPIClientMock) SecretRemove(ctx context.Context, id string) error {
397
+	return nil
398
+}
399
+func (s secretAPIClientMock) SecretInspectWithRaw(ctx context.Context, name string) (swarm.Secret, []byte, error) {
400
+	return swarm.Secret{}, []byte{}, nil
401
+}
402
+func (s secretAPIClientMock) SecretUpdate(ctx context.Context, id string, version swarm.Version, secret swarm.SecretSpec) error {
403
+	return nil
404
+}
405
+
406
+// TestUpdateSecretUpdateInPlace tests the ability to update the "target" of an secret with "docker service update"
407
+// by combining "--secret-rm" and "--secret-add" for the same secret.
408
+func TestUpdateSecretUpdateInPlace(t *testing.T) {
409
+	apiClient := secretAPIClientMock{
410
+		listResult: []swarm.Secret{
411
+			{
412
+				ID:   "tn9qiblgnuuut11eufquw5dev",
413
+				Spec: swarm.SecretSpec{Annotations: swarm.Annotations{Name: "foo"}},
414
+			},
415
+		},
416
+	}
417
+
418
+	flags := newUpdateCommand(nil).Flags()
419
+	flags.Set("secret-add", "source=foo,target=foo2")
420
+	flags.Set("secret-rm", "foo")
421
+
422
+	secrets := []*swarm.SecretReference{
423
+		{
424
+			File: &swarm.SecretReferenceFileTarget{
425
+				Name: "foo",
426
+				UID:  "0",
427
+				GID:  "0",
428
+				Mode: 292,
429
+			},
430
+			SecretID:   "tn9qiblgnuuut11eufquw5dev",
431
+			SecretName: "foo",
432
+		},
433
+	}
434
+
435
+	updatedSecrets, err := getUpdatedSecrets(apiClient, flags, secrets)
436
+
437
+	assert.Equal(t, err, nil)
438
+	assert.Equal(t, len(updatedSecrets), 1)
439
+	assert.Equal(t, updatedSecrets[0].SecretID, "tn9qiblgnuuut11eufquw5dev")
440
+	assert.Equal(t, updatedSecrets[0].SecretName, "foo")
441
+	assert.Equal(t, updatedSecrets[0].File.Name, "foo2")
442
+}
... ...
@@ -166,4 +166,5 @@ type SecretAPIClient interface {
166 166
 	SecretCreate(ctx context.Context, secret swarm.SecretSpec) (types.SecretCreateResponse, error)
167 167
 	SecretRemove(ctx context.Context, id string) error
168 168
 	SecretInspectWithRaw(ctx context.Context, name string) (swarm.Secret, []byte, error)
169
+	SecretUpdate(ctx context.Context, id string, version swarm.Version, secret swarm.SecretSpec) error
169 170
 }
170 171
new file mode 100644
... ...
@@ -0,0 +1,19 @@
0
+package client
1
+
2
+import (
3
+	"net/url"
4
+	"strconv"
5
+
6
+	"github.com/docker/docker/api/types/swarm"
7
+	"golang.org/x/net/context"
8
+)
9
+
10
+// SecretUpdate updates a Secret. Currently, the only part of a secret spec
11
+// which can be updated is Labels.
12
+func (cli *Client) SecretUpdate(ctx context.Context, id string, version swarm.Version, secret swarm.SecretSpec) error {
13
+	query := url.Values{}
14
+	query.Set("version", strconv.FormatUint(version.Index, 10))
15
+	resp, err := cli.post(ctx, "/secrets/"+id+"/update", query, secret, nil)
16
+	ensureReaderClosed(resp)
17
+	return err
18
+}
0 19
new file mode 100644
... ...
@@ -0,0 +1,49 @@
0
+package client
1
+
2
+import (
3
+	"bytes"
4
+	"fmt"
5
+	"io/ioutil"
6
+	"net/http"
7
+	"strings"
8
+	"testing"
9
+
10
+	"golang.org/x/net/context"
11
+
12
+	"github.com/docker/docker/api/types/swarm"
13
+)
14
+
15
+func TestSecretUpdateError(t *testing.T) {
16
+	client := &Client{
17
+		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
18
+	}
19
+
20
+	err := client.SecretUpdate(context.Background(), "secret_id", swarm.Version{}, swarm.SecretSpec{})
21
+	if err == nil || err.Error() != "Error response from daemon: Server error" {
22
+		t.Fatalf("expected a Server Error, got %v", err)
23
+	}
24
+}
25
+
26
+func TestSecretUpdate(t *testing.T) {
27
+	expectedURL := "/secrets/secret_id/update"
28
+
29
+	client := &Client{
30
+		client: newMockClient(func(req *http.Request) (*http.Response, error) {
31
+			if !strings.HasPrefix(req.URL.Path, expectedURL) {
32
+				return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
33
+			}
34
+			if req.Method != "POST" {
35
+				return nil, fmt.Errorf("expected POST method, got %s", req.Method)
36
+			}
37
+			return &http.Response{
38
+				StatusCode: http.StatusOK,
39
+				Body:       ioutil.NopCloser(bytes.NewReader([]byte("body"))),
40
+			}, nil
41
+		}),
42
+	}
43
+
44
+	err := client.SecretUpdate(context.Background(), "secret_id", swarm.Version{}, swarm.SecretSpec{})
45
+	if err != nil {
46
+		t.Fatal(err)
47
+	}
48
+}