Browse code

client/secrets: Wrap results and options

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Sebastiaan van Stijn authored on 2025/10/22 04:33:26
Showing 26 changed files
... ...
@@ -203,11 +203,11 @@ type VolumeAPIClient interface {
203 203
 
204 204
 // SecretAPIClient defines API client methods for secrets
205 205
 type SecretAPIClient interface {
206
-	SecretList(ctx context.Context, options SecretListOptions) ([]swarm.Secret, error)
207
-	SecretCreate(ctx context.Context, secret swarm.SecretSpec) (swarm.SecretCreateResponse, error)
208
-	SecretRemove(ctx context.Context, id string) error
209
-	SecretInspectWithRaw(ctx context.Context, name string) (swarm.Secret, []byte, error)
210
-	SecretUpdate(ctx context.Context, id string, version swarm.Version, secret swarm.SecretSpec) error
206
+	SecretList(ctx context.Context, options SecretListOptions) (SecretListResult, error)
207
+	SecretCreate(ctx context.Context, options SecretCreateOptions) (SecretCreateResult, error)
208
+	SecretRemove(ctx context.Context, id string, options SecretRemoveOptions) (SecretRemoveResult, error)
209
+	SecretInspect(ctx context.Context, id string, options SecretInspectOptions) (SecretInspectResult, error)
210
+	SecretUpdate(ctx context.Context, id string, options SecretUpdateOptions) (SecretUpdateResult, error)
211 211
 }
212 212
 
213 213
 // ConfigAPIClient defines API client methods for configs
... ...
@@ -7,15 +7,28 @@ import (
7 7
 	"github.com/moby/moby/api/types/swarm"
8 8
 )
9 9
 
10
+// SecretCreateOptions holds options for creating a secret.
11
+type SecretCreateOptions struct {
12
+	Spec swarm.SecretSpec
13
+}
14
+
15
+// SecretCreateResult holds the result from the [Client.SecretCreate] method.
16
+type SecretCreateResult struct {
17
+	ID string
18
+}
19
+
10 20
 // SecretCreate creates a new secret.
11
-func (cli *Client) SecretCreate(ctx context.Context, secret swarm.SecretSpec) (swarm.SecretCreateResponse, error) {
12
-	resp, err := cli.post(ctx, "/secrets/create", nil, secret, nil)
21
+func (cli *Client) SecretCreate(ctx context.Context, options SecretCreateOptions) (SecretCreateResult, error) {
22
+	resp, err := cli.post(ctx, "/secrets/create", nil, options.Spec, nil)
13 23
 	defer ensureReaderClosed(resp)
14 24
 	if err != nil {
15
-		return swarm.SecretCreateResponse{}, err
25
+		return SecretCreateResult{}, err
16 26
 	}
17 27
 
18
-	var response swarm.SecretCreateResponse
19
-	err = json.NewDecoder(resp.Body).Decode(&response)
20
-	return response, err
28
+	var out swarm.ConfigCreateResponse
29
+	err = json.NewDecoder(resp.Body).Decode(&out)
30
+	if err != nil {
31
+		return SecretCreateResult{}, err
32
+	}
33
+	return SecretCreateResult{ID: out.ID}, nil
21 34
 }
... ...
@@ -17,7 +17,7 @@ import (
17 17
 func TestSecretCreateError(t *testing.T) {
18 18
 	client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
19 19
 	assert.NilError(t, err)
20
-	_, err = client.SecretCreate(context.Background(), swarm.SecretSpec{})
20
+	_, err = client.SecretCreate(context.Background(), SecretCreateOptions{})
21 21
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
22 22
 }
23 23
 
... ...
@@ -40,7 +40,7 @@ func TestSecretCreate(t *testing.T) {
40 40
 	}))
41 41
 	assert.NilError(t, err)
42 42
 
43
-	r, err := client.SecretCreate(context.Background(), swarm.SecretSpec{})
43
+	r, err := client.SecretCreate(context.Background(), SecretCreateOptions{})
44 44
 	assert.NilError(t, err)
45 45
 	assert.Check(t, is.Equal(r.ID, "test_secret"))
46 46
 }
... ...
@@ -1,34 +1,35 @@
1 1
 package client
2 2
 
3 3
 import (
4
-	"bytes"
5 4
 	"context"
6
-	"encoding/json"
7
-	"io"
8 5
 
9 6
 	"github.com/moby/moby/api/types/swarm"
10 7
 )
11 8
 
12
-// SecretInspectWithRaw returns the secret information with raw data
13
-func (cli *Client) SecretInspectWithRaw(ctx context.Context, id string) (swarm.Secret, []byte, error) {
9
+// SecretInspectOptions holds options for inspecting a secret.
10
+type SecretInspectOptions struct {
11
+	// Add future optional parameters here
12
+}
13
+
14
+// SecretInspectResult holds the result from the [Client.SecretInspect]. method.
15
+type SecretInspectResult struct {
16
+	Secret swarm.Secret
17
+	Raw    []byte
18
+}
19
+
20
+// SecretInspect returns the secret information with raw data.
21
+func (cli *Client) SecretInspect(ctx context.Context, id string, options SecretInspectOptions) (SecretInspectResult, error) {
14 22
 	id, err := trimID("secret", id)
15 23
 	if err != nil {
16
-		return swarm.Secret{}, nil, err
24
+		return SecretInspectResult{}, err
17 25
 	}
18 26
 	resp, err := cli.get(ctx, "/secrets/"+id, nil, nil)
19 27
 	defer ensureReaderClosed(resp)
20 28
 	if err != nil {
21
-		return swarm.Secret{}, nil, err
29
+		return SecretInspectResult{}, err
22 30
 	}
23 31
 
24
-	body, err := io.ReadAll(resp.Body)
25
-	if err != nil {
26
-		return swarm.Secret{}, nil, err
27
-	}
28
-
29
-	var secret swarm.Secret
30
-	rdr := bytes.NewReader(body)
31
-	err = json.NewDecoder(rdr).Decode(&secret)
32
-
33
-	return secret, body, err
32
+	var out SecretInspectResult
33
+	out.Raw, err = decodeWithRaw(resp, &out.Secret)
34
+	return out, err
34 35
 }
... ...
@@ -19,7 +19,7 @@ func TestSecretInspectError(t *testing.T) {
19 19
 	client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
20 20
 	assert.NilError(t, err)
21 21
 
22
-	_, _, err = client.SecretInspectWithRaw(context.Background(), "nothing")
22
+	_, err = client.SecretInspect(context.Background(), "nothing", SecretInspectOptions{})
23 23
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
24 24
 }
25 25
 
... ...
@@ -27,7 +27,7 @@ func TestSecretInspectSecretNotFound(t *testing.T) {
27 27
 	client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusNotFound, "Server error")))
28 28
 	assert.NilError(t, err)
29 29
 
30
-	_, _, err = client.SecretInspectWithRaw(context.Background(), "unknown")
30
+	_, err = client.SecretInspect(context.Background(), "unknown", SecretInspectOptions{})
31 31
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
32 32
 }
33 33
 
... ...
@@ -36,11 +36,11 @@ func TestSecretInspectWithEmptyID(t *testing.T) {
36 36
 		return nil, errors.New("should not make request")
37 37
 	}))
38 38
 	assert.NilError(t, err)
39
-	_, _, err = client.SecretInspectWithRaw(context.Background(), "")
39
+	_, err = client.SecretInspect(context.Background(), "", SecretInspectOptions{})
40 40
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
41 41
 	assert.Check(t, is.ErrorContains(err, "value is empty"))
42 42
 
43
-	_, _, err = client.SecretInspectWithRaw(context.Background(), "    ")
43
+	_, err = client.SecretInspect(context.Background(), "    ", SecretInspectOptions{})
44 44
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
45 45
 	assert.Check(t, is.ErrorContains(err, "value is empty"))
46 46
 }
... ...
@@ -64,7 +64,7 @@ func TestSecretInspect(t *testing.T) {
64 64
 	}))
65 65
 	assert.NilError(t, err)
66 66
 
67
-	secretInspect, _, err := client.SecretInspectWithRaw(context.Background(), "secret_id")
67
+	res, err := client.SecretInspect(context.Background(), "secret_id", SecretInspectOptions{})
68 68
 	assert.NilError(t, err)
69
-	assert.Check(t, is.Equal(secretInspect.ID, "secret_id"))
69
+	assert.Check(t, is.Equal(res.Secret.ID, "secret_id"))
70 70
 }
... ...
@@ -8,18 +8,31 @@ import (
8 8
 	"github.com/moby/moby/api/types/swarm"
9 9
 )
10 10
 
11
+// SecretListOptions holds parameters to list secrets
12
+type SecretListOptions struct {
13
+	Filters Filters
14
+}
15
+
16
+// SecretListResult holds the result from the [client.SecretList] method.
17
+type SecretListResult struct {
18
+	Items []swarm.Secret
19
+}
20
+
11 21
 // SecretList returns the list of secrets.
12
-func (cli *Client) SecretList(ctx context.Context, options SecretListOptions) ([]swarm.Secret, error) {
22
+func (cli *Client) SecretList(ctx context.Context, options SecretListOptions) (SecretListResult, error) {
13 23
 	query := url.Values{}
14
-
15 24
 	options.Filters.updateURLValues(query)
25
+
16 26
 	resp, err := cli.get(ctx, "/secrets", query, nil)
17 27
 	defer ensureReaderClosed(resp)
18 28
 	if err != nil {
19
-		return nil, err
29
+		return SecretListResult{}, err
20 30
 	}
21 31
 
22
-	var secrets []swarm.Secret
23
-	err = json.NewDecoder(resp.Body).Decode(&secrets)
24
-	return secrets, err
32
+	var out SecretListResult
33
+	err = json.NewDecoder(resp.Body).Decode(&out.Items)
34
+	if err != nil {
35
+		return SecretListResult{}, err
36
+	}
37
+	return out, nil
25 38
 }
26 39
deleted file mode 100644
... ...
@@ -1,6 +0,0 @@
1
-package client
2
-
3
-// SecretListOptions holds parameters to list secrets
4
-type SecretListOptions struct {
5
-	Filters Filters
6
-}
... ...
@@ -75,8 +75,8 @@ func TestSecretList(t *testing.T) {
75 75
 		}))
76 76
 		assert.NilError(t, err)
77 77
 
78
-		secrets, err := client.SecretList(context.Background(), listCase.options)
78
+		res, err := client.SecretList(context.Background(), listCase.options)
79 79
 		assert.NilError(t, err)
80
-		assert.Check(t, is.Len(secrets, 2))
80
+		assert.Check(t, is.Len(res.Items, 2))
81 81
 	}
82 82
 }
... ...
@@ -2,13 +2,24 @@ package client
2 2
 
3 3
 import "context"
4 4
 
5
+type SecretRemoveOptions struct {
6
+	// Add future optional parameters here
7
+}
8
+
9
+type SecretRemoveResult struct {
10
+	// Add future fields here
11
+}
12
+
5 13
 // SecretRemove removes a secret.
6
-func (cli *Client) SecretRemove(ctx context.Context, id string) error {
14
+func (cli *Client) SecretRemove(ctx context.Context, id string, options SecretRemoveOptions) (SecretRemoveResult, error) {
7 15
 	id, err := trimID("secret", id)
8 16
 	if err != nil {
9
-		return err
17
+		return SecretRemoveResult{}, err
10 18
 	}
11 19
 	resp, err := cli.delete(ctx, "/secrets/"+id, nil, nil)
12 20
 	defer ensureReaderClosed(resp)
13
-	return err
21
+	if err != nil {
22
+		return SecretRemoveResult{}, err
23
+	}
24
+	return SecretRemoveResult{}, nil
14 25
 }
... ...
@@ -16,14 +16,14 @@ func TestSecretRemoveError(t *testing.T) {
16 16
 	client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
17 17
 	assert.NilError(t, err)
18 18
 
19
-	err = client.SecretRemove(context.Background(), "secret_id")
19
+	_, err = client.SecretRemove(context.Background(), "secret_id", SecretRemoveOptions{})
20 20
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
21 21
 
22
-	err = client.SecretRemove(context.Background(), "")
22
+	_, err = client.SecretRemove(context.Background(), "", SecretRemoveOptions{})
23 23
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
24 24
 	assert.Check(t, is.ErrorContains(err, "value is empty"))
25 25
 
26
-	err = client.SecretRemove(context.Background(), "   ")
26
+	_, err = client.SecretRemove(context.Background(), "   ", SecretRemoveOptions{})
27 27
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
28 28
 	assert.Check(t, is.ErrorContains(err, "value is empty"))
29 29
 }
... ...
@@ -42,6 +42,6 @@ func TestSecretRemove(t *testing.T) {
42 42
 	}))
43 43
 	assert.NilError(t, err)
44 44
 
45
-	err = client.SecretRemove(context.Background(), "secret_id")
45
+	_, err = client.SecretRemove(context.Background(), "secret_id", SecretRemoveOptions{})
46 46
 	assert.NilError(t, err)
47 47
 }
... ...
@@ -7,15 +7,26 @@ import (
7 7
 	"github.com/moby/moby/api/types/swarm"
8 8
 )
9 9
 
10
+// SecretUpdateOptions holds options for updating a secret.
11
+type SecretUpdateOptions struct {
12
+	Version swarm.Version
13
+	Spec    swarm.SecretSpec
14
+}
15
+
16
+type SecretUpdateResult struct{}
17
+
10 18
 // SecretUpdate attempts to update a secret.
11
-func (cli *Client) SecretUpdate(ctx context.Context, id string, version swarm.Version, secret swarm.SecretSpec) error {
19
+func (cli *Client) SecretUpdate(ctx context.Context, id string, options SecretUpdateOptions) (SecretUpdateResult, error) {
12 20
 	id, err := trimID("secret", id)
13 21
 	if err != nil {
14
-		return err
22
+		return SecretUpdateResult{}, err
15 23
 	}
16 24
 	query := url.Values{}
17
-	query.Set("version", version.String())
18
-	resp, err := cli.post(ctx, "/secrets/"+id+"/update", query, secret, nil)
25
+	query.Set("version", options.Version.String())
26
+	resp, err := cli.post(ctx, "/secrets/"+id+"/update", query, options.Spec, nil)
19 27
 	defer ensureReaderClosed(resp)
20
-	return err
28
+	if err != nil {
29
+		return SecretUpdateResult{}, err
30
+	}
31
+	return SecretUpdateResult{}, nil
21 32
 }
... ...
@@ -8,7 +8,6 @@ import (
8 8
 	"testing"
9 9
 
10 10
 	cerrdefs "github.com/containerd/errdefs"
11
-	"github.com/moby/moby/api/types/swarm"
12 11
 	"gotest.tools/v3/assert"
13 12
 	is "gotest.tools/v3/assert/cmp"
14 13
 )
... ...
@@ -17,14 +16,14 @@ func TestSecretUpdateError(t *testing.T) {
17 17
 	client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
18 18
 	assert.NilError(t, err)
19 19
 
20
-	err = client.SecretUpdate(context.Background(), "secret_id", swarm.Version{}, swarm.SecretSpec{})
20
+	_, err = client.SecretUpdate(context.Background(), "secret_id", SecretUpdateOptions{})
21 21
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
22 22
 
23
-	err = client.SecretUpdate(context.Background(), "", swarm.Version{}, swarm.SecretSpec{})
23
+	_, err = client.SecretUpdate(context.Background(), "", SecretUpdateOptions{})
24 24
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
25 25
 	assert.Check(t, is.ErrorContains(err, "value is empty"))
26 26
 
27
-	err = client.SecretUpdate(context.Background(), "    ", swarm.Version{}, swarm.SecretSpec{})
27
+	_, err = client.SecretUpdate(context.Background(), "    ", SecretUpdateOptions{})
28 28
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
29 29
 	assert.Check(t, is.ErrorContains(err, "value is empty"))
30 30
 }
... ...
@@ -43,6 +42,6 @@ func TestSecretUpdate(t *testing.T) {
43 43
 	}))
44 44
 	assert.NilError(t, err)
45 45
 
46
-	err = client.SecretUpdate(context.Background(), "secret_id", swarm.Version{}, swarm.SecretSpec{})
46
+	_, err = client.SecretUpdate(context.Background(), "secret_id", SecretUpdateOptions{})
47 47
 	assert.NilError(t, err)
48 48
 }
... ...
@@ -12,6 +12,7 @@ import (
12 12
 	"github.com/moby/moby/api/types/container"
13 13
 	"github.com/moby/moby/api/types/mount"
14 14
 	"github.com/moby/moby/api/types/swarm"
15
+	"github.com/moby/moby/client"
15 16
 	"github.com/moby/moby/v2/integration-cli/checker"
16 17
 	"github.com/moby/moby/v2/internal/testutil"
17 18
 	"gotest.tools/v3/assert"
... ...
@@ -71,16 +72,20 @@ func (s *DockerSwarmSuite) TestServiceCreateMountVolume(c *testing.T) {
71 71
 func (s *DockerSwarmSuite) TestServiceCreateWithSecretSimple(c *testing.T) {
72 72
 	ctx := testutil.GetContext(c)
73 73
 	d := s.AddDaemon(ctx, c, true, true)
74
+	apiClient := d.NewClientT(c)
74 75
 
75 76
 	serviceName := "test-service-secret"
76 77
 	testName := "test_secret"
77
-	id := d.CreateSecret(c, swarm.SecretSpec{
78
-		Annotations: swarm.Annotations{
79
-			Name: testName,
78
+	scr, err := apiClient.SecretCreate(ctx, client.SecretCreateOptions{
79
+		Spec: swarm.SecretSpec{
80
+			Annotations: swarm.Annotations{
81
+				Name: testName,
82
+			},
83
+			Data: []byte("TESTINGDATA"),
80 84
 		},
81
-		Data: []byte("TESTINGDATA"),
82 85
 	})
83
-	assert.Assert(c, id != "", "secrets: %s", id)
86
+	assert.NilError(c, err)
87
+	assert.Assert(c, scr.ID != "", "secrets: %s", scr.ID)
84 88
 
85 89
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", serviceName, "--secret", testName, "busybox", "top")
86 90
 	assert.NilError(c, err, out)
... ...
@@ -100,7 +105,8 @@ func (s *DockerSwarmSuite) TestServiceCreateWithSecretSimple(c *testing.T) {
100 100
 
101 101
 	out, err = d.Cmd("service", "rm", serviceName)
102 102
 	assert.NilError(c, err, out)
103
-	d.DeleteSecret(c, testName)
103
+	_, err = apiClient.SecretRemove(c.Context(), testName, client.SecretRemoveOptions{})
104
+	assert.NilError(c, err)
104 105
 }
105 106
 
106 107
 func (s *DockerSwarmSuite) TestServiceCreateWithSecretSourceTargetPaths(c *testing.T) {
... ...
@@ -116,15 +122,18 @@ func (s *DockerSwarmSuite) TestServiceCreateWithSecretSourceTargetPaths(c *testi
116 116
 
117 117
 	var secretFlags []string
118 118
 
119
+	apiClient := d.NewClientT(c)
119 120
 	for testName, testTarget := range testPaths {
120
-		id := d.CreateSecret(c, swarm.SecretSpec{
121
-			Annotations: swarm.Annotations{
122
-				Name: testName,
121
+		scr, err := apiClient.SecretCreate(ctx, client.SecretCreateOptions{
122
+			Spec: swarm.SecretSpec{
123
+				Annotations: swarm.Annotations{
124
+					Name: testName,
125
+				},
126
+				Data: []byte("TESTINGDATA " + testName + " " + testTarget),
123 127
 			},
124
-			Data: []byte("TESTINGDATA " + testName + " " + testTarget),
125 128
 		})
126
-		assert.Assert(c, id != "", "secrets: %s", id)
127
-
129
+		assert.NilError(c, err)
130
+		assert.Assert(c, scr.ID != "", "secrets: %s", scr.ID)
128 131
 		secretFlags = append(secretFlags, "--secret", fmt.Sprintf("source=%s,target=%s", testName, testTarget))
129 132
 	}
130 133
 
... ...
@@ -174,13 +183,17 @@ func (s *DockerSwarmSuite) TestServiceCreateWithSecretReferencedTwice(c *testing
174 174
 	ctx := testutil.GetContext(c)
175 175
 	d := s.AddDaemon(ctx, c, true, true)
176 176
 
177
-	id := d.CreateSecret(c, swarm.SecretSpec{
178
-		Annotations: swarm.Annotations{
179
-			Name: "mysecret",
177
+	apiClient := d.NewClientT(c)
178
+	scr, err := apiClient.SecretCreate(ctx, client.SecretCreateOptions{
179
+		Spec: swarm.SecretSpec{
180
+			Annotations: swarm.Annotations{
181
+				Name: "mysecret",
182
+			},
183
+			Data: []byte("TESTINGDATA"),
180 184
 		},
181
-		Data: []byte("TESTINGDATA"),
182 185
 	})
183
-	assert.Assert(c, id != "", "secrets: %s", id)
186
+	assert.NilError(c, err)
187
+	assert.Assert(c, scr.ID != "", "secrets: %s", scr.ID)
184 188
 
185 189
 	serviceName := "svc"
186 190
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", serviceName, "--secret", "source=mysecret,target=target1", "--secret", "source=mysecret,target=target2", "busybox", "top")
... ...
@@ -21,6 +21,7 @@ import (
21 21
 	"github.com/cloudflare/cfssl/helpers"
22 22
 	"github.com/moby/moby/api/types/swarm"
23 23
 	"github.com/moby/moby/api/types/versions"
24
+	"github.com/moby/moby/client"
24 25
 	"github.com/moby/moby/v2/daemon/libnetwork/driverapi"
25 26
 	"github.com/moby/moby/v2/daemon/libnetwork/ipamapi"
26 27
 	remoteipam "github.com/moby/moby/v2/daemon/libnetwork/ipams/remote/api"
... ...
@@ -1985,20 +1986,27 @@ func (s *DockerSwarmSuite) TestSwarmClusterEventsNetwork(c *testing.T) {
1985 1985
 func (s *DockerSwarmSuite) TestSwarmClusterEventsSecret(c *testing.T) {
1986 1986
 	ctx := testutil.GetContext(c)
1987 1987
 	d := s.AddDaemon(ctx, c, true, true)
1988
+	apiClient := d.NewClientT(c)
1988 1989
 
1989 1990
 	testName := "test_secret"
1990
-	id := d.CreateSecret(c, swarm.SecretSpec{
1991
-		Annotations: swarm.Annotations{
1992
-			Name: testName,
1991
+	scr, err := apiClient.SecretCreate(ctx, client.SecretCreateOptions{
1992
+		Spec: swarm.SecretSpec{
1993
+			Annotations: swarm.Annotations{
1994
+				Name: testName,
1995
+			},
1996
+			Data: []byte("TESTINGDATA"),
1993 1997
 		},
1994
-		Data: []byte("TESTINGDATA"),
1995 1998
 	})
1996
-	assert.Assert(c, id != "", "secrets: %s", id)
1999
+	assert.NilError(c, err)
2000
+	assert.Assert(c, scr.ID != "", "secrets: %s", scr.ID)
2001
+	id := scr.ID
1997 2002
 
1998 2003
 	waitForEvent(c, d, "0", "-f scope=swarm", "secret create "+id, defaultRetryCount)
1999 2004
 
2000 2005
 	t1 := daemonUnixTime(c)
2001
-	d.DeleteSecret(c, id)
2006
+	_, err = apiClient.SecretRemove(c.Context(), id, client.SecretRemoveOptions{})
2007
+	assert.NilError(c, err)
2008
+
2002 2009
 	// filtered by secret
2003 2010
 	waitForEvent(c, d, t1, "-f type=secret", "secret remove "+id, defaultRetryCount)
2004 2011
 }
... ...
@@ -236,7 +236,9 @@ func TestTemplatedConfig(t *testing.T) {
236 236
 		},
237 237
 		Data: []byte("this is a secret"),
238 238
 	}
239
-	referencedSecret, err := c.SecretCreate(ctx, referencedSecretSpec)
239
+	referencedSecret, err := c.SecretCreate(ctx, client.SecretCreateOptions{
240
+		Spec: referencedSecretSpec,
241
+	})
240 242
 	assert.Check(t, err)
241 243
 
242 244
 	referencedConfigName := "referencedconfig-" + t.Name()
... ...
@@ -32,14 +32,14 @@ func TestSecretInspect(t *testing.T) {
32 32
 	testName := t.Name()
33 33
 	secretID := createSecret(ctx, t, c, testName, []byte("TESTINGDATA"), nil)
34 34
 
35
-	insp, body, err := c.SecretInspectWithRaw(ctx, secretID)
35
+	result, err := c.SecretInspect(ctx, secretID, client.SecretInspectOptions{})
36 36
 	assert.NilError(t, err)
37
-	assert.Check(t, is.Equal(insp.Spec.Name, testName))
37
+	assert.Check(t, is.Equal(result.Secret.Spec.Name, testName))
38 38
 
39 39
 	var secret swarmtypes.Secret
40
-	err = json.Unmarshal(body, &secret)
40
+	err = json.Unmarshal(result.Raw, &secret)
41 41
 	assert.NilError(t, err)
42
-	assert.Check(t, is.DeepEqual(secret, insp))
42
+	assert.Check(t, is.DeepEqual(secret, result.Secret))
43 43
 }
44 44
 
45 45
 func TestSecretList(t *testing.T) {
... ...
@@ -51,12 +51,12 @@ func TestSecretList(t *testing.T) {
51 51
 	c := d.NewClientT(t)
52 52
 	defer c.Close()
53 53
 
54
-	configs, err := c.SecretList(ctx, client.SecretListOptions{})
54
+	result, err := c.SecretList(ctx, client.SecretListOptions{})
55 55
 	assert.NilError(t, err)
56
-	assert.Check(t, is.Equal(len(configs), 0))
56
+	assert.Check(t, is.Equal(len(result.Items), 0))
57 57
 
58
-	testName0 := "test0_" + t.Name()
59
-	testName1 := "test1_" + t.Name()
58
+	testName0 := "test0-" + t.Name()
59
+	testName1 := "test1-" + t.Name()
60 60
 	testNames := []string{testName0, testName1}
61 61
 	sort.Strings(testNames)
62 62
 
... ...
@@ -67,58 +67,66 @@ func TestSecretList(t *testing.T) {
67 67
 	secret1ID := createSecret(ctx, t, c, testName1, []byte("TESTINGDATA1"), map[string]string{"type": "production"})
68 68
 
69 69
 	// test by `secret ls`
70
-	entries, err := c.SecretList(ctx, client.SecretListOptions{})
70
+	res, err := c.SecretList(ctx, client.SecretListOptions{})
71 71
 	assert.NilError(t, err)
72
-	assert.Check(t, is.DeepEqual(secretNamesFromList(entries), testNames))
72
+	assert.Check(t, is.DeepEqual(namesFromList(res.Items), testNames))
73 73
 
74 74
 	testCases := []struct {
75
+		desc     string
75 76
 		filters  client.Filters
76 77
 		expected []string
77 78
 	}{
78
-		// test filter by name `secret ls --filter name=xxx`
79 79
 		{
80
+			desc:     "test filter by name",
80 81
 			filters:  make(client.Filters).Add("name", testName0),
81 82
 			expected: []string{testName0},
82 83
 		},
83
-		// test filter by id `secret ls --filter id=xxx`
84 84
 		{
85
+			desc:     "test filter by id",
85 86
 			filters:  make(client.Filters).Add("id", secret1ID),
86 87
 			expected: []string{testName1},
87 88
 		},
88
-		// test filter by label `secret ls --filter label=xxx`
89 89
 		{
90
+			desc:     "test filter by label key only",
90 91
 			filters:  make(client.Filters).Add("label", "type"),
91 92
 			expected: testNames,
92 93
 		},
93 94
 		{
95
+			desc:     "test filter by label key=value " + testName0,
94 96
 			filters:  make(client.Filters).Add("label", "type=test"),
95 97
 			expected: []string{testName0},
96 98
 		},
97 99
 		{
100
+			desc:     "test filter by label key=value " + testName1,
98 101
 			filters:  make(client.Filters).Add("label", "type=production"),
99 102
 			expected: []string{testName1},
100 103
 		},
101 104
 	}
102 105
 	for _, tc := range testCases {
103
-		entries, err = c.SecretList(ctx, client.SecretListOptions{
104
-			Filters: tc.filters,
106
+		t.Run(tc.desc, func(t *testing.T) {
107
+			ctx := testutil.StartSpan(ctx, t)
108
+			res, err = c.SecretList(ctx, client.SecretListOptions{
109
+				Filters: tc.filters,
110
+			})
111
+			assert.NilError(t, err)
112
+			assert.Check(t, is.DeepEqual(namesFromList(res.Items), tc.expected))
105 113
 		})
106
-		assert.NilError(t, err)
107
-		assert.Check(t, is.DeepEqual(secretNamesFromList(entries), tc.expected))
108 114
 	}
109 115
 }
110 116
 
111
-func createSecret(ctx context.Context, t *testing.T, client client.APIClient, name string, data []byte, labels map[string]string) string {
112
-	secret, err := client.SecretCreate(ctx, swarmtypes.SecretSpec{
113
-		Annotations: swarmtypes.Annotations{
114
-			Name:   name,
115
-			Labels: labels,
117
+func createSecret(ctx context.Context, t *testing.T, apiClient client.APIClient, name string, data []byte, labels map[string]string) string {
118
+	result, err := apiClient.SecretCreate(ctx, client.SecretCreateOptions{
119
+		Spec: swarmtypes.SecretSpec{
120
+			Annotations: swarmtypes.Annotations{
121
+				Name:   name,
122
+				Labels: labels,
123
+			},
124
+			Data: data,
116 125
 		},
117
-		Data: data,
118 126
 	})
119 127
 	assert.NilError(t, err)
120
-	assert.Check(t, secret.ID != "")
121
-	return secret.ID
128
+	assert.Check(t, result.ID != "")
129
+	return result.ID
122 130
 }
123 131
 
124 132
 func TestSecretsCreateAndDelete(t *testing.T) {
... ...
@@ -134,23 +142,25 @@ func TestSecretsCreateAndDelete(t *testing.T) {
134 134
 	secretID := createSecret(ctx, t, c, testName, []byte("TESTINGDATA"), nil)
135 135
 
136 136
 	// create an already existing secret, daemon should return a status code of 409
137
-	_, err := c.SecretCreate(ctx, swarmtypes.SecretSpec{
138
-		Annotations: swarmtypes.Annotations{
139
-			Name: testName,
137
+	_, err := c.SecretCreate(ctx, client.SecretCreateOptions{
138
+		Spec: swarmtypes.SecretSpec{
139
+			Annotations: swarmtypes.Annotations{
140
+				Name: testName,
141
+			},
142
+			Data: []byte("TESTINGDATA"),
140 143
 		},
141
-		Data: []byte("TESTINGDATA"),
142 144
 	})
143 145
 	assert.Check(t, cerrdefs.IsConflict(err))
144 146
 	assert.Check(t, is.ErrorContains(err, testName))
145 147
 
146
-	err = c.SecretRemove(ctx, secretID)
148
+	_, err = c.SecretRemove(ctx, secretID, client.SecretRemoveOptions{})
147 149
 	assert.NilError(t, err)
148 150
 
149
-	_, _, err = c.SecretInspectWithRaw(ctx, secretID)
151
+	_, err = c.SecretInspect(ctx, secretID, client.SecretInspectOptions{})
150 152
 	assert.Check(t, cerrdefs.IsNotFound(err))
151 153
 	assert.Check(t, is.ErrorContains(err, secretID))
152 154
 
153
-	err = c.SecretRemove(ctx, "non-existing")
155
+	_, err = c.SecretRemove(ctx, "non-existing", client.SecretRemoveOptions{})
154 156
 	assert.Check(t, cerrdefs.IsNotFound(err))
155 157
 	assert.Check(t, is.ErrorContains(err, "non-existing"))
156 158
 
... ...
@@ -160,12 +170,12 @@ func TestSecretsCreateAndDelete(t *testing.T) {
160 160
 		"key2": "value2",
161 161
 	})
162 162
 
163
-	insp, _, err := c.SecretInspectWithRaw(ctx, secretID)
163
+	result, err := c.SecretInspect(ctx, secretID, client.SecretInspectOptions{})
164 164
 	assert.NilError(t, err)
165
-	assert.Check(t, is.Equal(insp.Spec.Name, testName))
166
-	assert.Check(t, is.Equal(len(insp.Spec.Labels), 2))
167
-	assert.Check(t, is.Equal(insp.Spec.Labels["key1"], "value1"))
168
-	assert.Check(t, is.Equal(insp.Spec.Labels["key2"], "value2"))
165
+	assert.Check(t, is.Equal(result.Secret.Spec.Name, testName))
166
+	assert.Check(t, is.Equal(len(result.Secret.Spec.Labels), 2))
167
+	assert.Check(t, is.Equal(result.Secret.Spec.Labels["key1"], "value1"))
168
+	assert.Check(t, is.Equal(result.Secret.Spec.Labels["key2"], "value2"))
169 169
 }
170 170
 
171 171
 func TestSecretsUpdate(t *testing.T) {
... ...
@@ -180,41 +190,53 @@ func TestSecretsUpdate(t *testing.T) {
180 180
 	testName := "test_secret_" + t.Name()
181 181
 	secretID := createSecret(ctx, t, c, testName, []byte("TESTINGDATA"), nil)
182 182
 
183
-	insp, _, err := c.SecretInspectWithRaw(ctx, secretID)
183
+	insp, err := c.SecretInspect(ctx, secretID, client.SecretInspectOptions{})
184 184
 	assert.NilError(t, err)
185
-	assert.Check(t, is.Equal(insp.ID, secretID))
185
+	assert.Check(t, is.Equal(insp.Secret.ID, secretID))
186 186
 
187 187
 	// test UpdateSecret with full ID
188
-	insp.Spec.Labels = map[string]string{"test": "test1"}
189
-	err = c.SecretUpdate(ctx, secretID, insp.Version, insp.Spec)
188
+	insp.Secret.Spec.Labels = map[string]string{"test": "test1"}
189
+	_, err = c.SecretUpdate(ctx, secretID, client.SecretUpdateOptions{
190
+		Version: insp.Secret.Version,
191
+		Spec:    insp.Secret.Spec,
192
+	})
190 193
 	assert.NilError(t, err)
191 194
 
192
-	insp, _, err = c.SecretInspectWithRaw(ctx, secretID)
195
+	insp, err = c.SecretInspect(ctx, secretID, client.SecretInspectOptions{})
193 196
 	assert.NilError(t, err)
194
-	assert.Check(t, is.Equal(insp.Spec.Labels["test"], "test1"))
197
+	assert.Check(t, is.Equal(insp.Secret.Spec.Labels["test"], "test1"))
195 198
 
196 199
 	// test UpdateSecret with full name
197
-	insp.Spec.Labels = map[string]string{"test": "test2"}
198
-	err = c.SecretUpdate(ctx, testName, insp.Version, insp.Spec)
200
+	insp.Secret.Spec.Labels = map[string]string{"test": "test2"}
201
+	_, err = c.SecretUpdate(ctx, testName, client.SecretUpdateOptions{
202
+		Version: insp.Secret.Version,
203
+		Spec:    insp.Secret.Spec,
204
+	})
199 205
 	assert.NilError(t, err)
200 206
 
201
-	insp, _, err = c.SecretInspectWithRaw(ctx, secretID)
207
+	insp, err = c.SecretInspect(ctx, secretID, client.SecretInspectOptions{})
202 208
 	assert.NilError(t, err)
203
-	assert.Check(t, is.Equal(insp.Spec.Labels["test"], "test2"))
209
+	assert.Check(t, is.Equal(insp.Secret.Spec.Labels["test"], "test2"))
204 210
 
205 211
 	// test UpdateSecret with prefix ID
206
-	insp.Spec.Labels = map[string]string{"test": "test3"}
207
-	err = c.SecretUpdate(ctx, secretID[:1], insp.Version, insp.Spec)
212
+	insp.Secret.Spec.Labels = map[string]string{"test": "test3"}
213
+	_, err = c.SecretUpdate(ctx, secretID[:1], client.SecretUpdateOptions{
214
+		Version: insp.Secret.Version,
215
+		Spec:    insp.Secret.Spec,
216
+	})
208 217
 	assert.NilError(t, err)
209 218
 
210
-	insp, _, err = c.SecretInspectWithRaw(ctx, secretID)
219
+	insp, err = c.SecretInspect(ctx, secretID, client.SecretInspectOptions{})
211 220
 	assert.NilError(t, err)
212
-	assert.Check(t, is.Equal(insp.Spec.Labels["test"], "test3"))
221
+	assert.Check(t, is.Equal(insp.Secret.Spec.Labels["test"], "test3"))
213 222
 
214 223
 	// test UpdateSecret in updating Data which is not supported in daemon
215 224
 	// this test will produce an error in func UpdateSecret
216
-	insp.Spec.Data = []byte("TESTINGDATA2")
217
-	err = c.SecretUpdate(ctx, secretID, insp.Version, insp.Spec)
225
+	insp.Secret.Spec.Data = []byte("TESTINGDATA2")
226
+	_, err = c.SecretUpdate(ctx, secretID, client.SecretUpdateOptions{
227
+		Version: insp.Secret.Version,
228
+		Spec:    insp.Secret.Spec,
229
+	})
218 230
 	assert.Check(t, cerrdefs.IsInvalidArgument(err))
219 231
 	assert.Check(t, is.ErrorContains(err, "only updates to Labels are allowed"))
220 232
 }
... ...
@@ -236,7 +258,9 @@ func TestTemplatedSecret(t *testing.T) {
236 236
 		},
237 237
 		Data: []byte("this is a secret"),
238 238
 	}
239
-	referencedSecret, err := c.SecretCreate(ctx, referencedSecretSpec)
239
+	referencedSecret, err := c.SecretCreate(ctx, client.SecretCreateOptions{
240
+		Spec: referencedSecretSpec,
241
+	})
240 242
 	assert.Check(t, err)
241 243
 
242 244
 	referencedConfigName := "referencedconfig_" + t.Name()
... ...
@@ -259,12 +283,15 @@ func TestTemplatedSecret(t *testing.T) {
259 259
 		Templating: &swarmtypes.Driver{
260 260
 			Name: "golang",
261 261
 		},
262
-		Data: []byte("SERVICE_NAME={{.Service.Name}}\n" +
263
-			"{{secret \"referencedsecrettarget\"}}\n" +
264
-			"{{config \"referencedconfigtarget\"}}\n"),
262
+		Data: []byte(`SERVICE_NAME={{.Service.Name}}
263
+{{secret "referencedsecrettarget"}}
264
+{{config "referencedconfigtarget"}}
265
+`),
265 266
 	}
266 267
 
267
-	templatedSecret, err := c.SecretCreate(ctx, secretSpec)
268
+	templatedSecret, err := c.SecretCreate(ctx, client.SecretCreateOptions{
269
+		Spec: secretSpec,
270
+	})
268 271
 	assert.Check(t, err)
269 272
 
270 273
 	const serviceName = "svc_templated_secret"
... ...
@@ -356,39 +383,39 @@ func TestSecretCreateResolve(t *testing.T) {
356 356
 	fakeName := secretID
357 357
 	fakeID := createSecret(ctx, t, c, fakeName, []byte("fake foo"), nil)
358 358
 
359
-	entries, err := c.SecretList(ctx, client.SecretListOptions{})
359
+	res, err := c.SecretList(ctx, client.SecretListOptions{})
360 360
 	assert.NilError(t, err)
361
-	assert.Check(t, is.Contains(secretNamesFromList(entries), testName))
362
-	assert.Check(t, is.Contains(secretNamesFromList(entries), fakeName))
361
+	assert.Check(t, is.Contains(namesFromList(res.Items), testName))
362
+	assert.Check(t, is.Contains(namesFromList(res.Items), fakeName))
363 363
 
364
-	err = c.SecretRemove(ctx, secretID)
364
+	_, err = c.SecretRemove(ctx, secretID, client.SecretRemoveOptions{})
365 365
 	assert.NilError(t, err)
366 366
 
367 367
 	// Fake one will remain
368
-	entries, err = c.SecretList(ctx, client.SecretListOptions{})
368
+	res, err = c.SecretList(ctx, client.SecretListOptions{})
369 369
 	assert.NilError(t, err)
370
-	assert.Assert(t, is.DeepEqual(secretNamesFromList(entries), []string{fakeName}))
370
+	assert.Assert(t, is.DeepEqual(namesFromList(res.Items), []string{fakeName}))
371 371
 
372 372
 	// Remove based on name prefix of the fake one should not work
373 373
 	// as search is only done based on:
374 374
 	// - Full ID
375 375
 	// - Full Name
376 376
 	// - Partial ID (prefix)
377
-	err = c.SecretRemove(ctx, fakeName[:5])
377
+	_, err = c.SecretRemove(ctx, fakeName[:5], client.SecretRemoveOptions{})
378 378
 	assert.Assert(t, err != nil)
379
-	entries, err = c.SecretList(ctx, client.SecretListOptions{})
379
+	res, err = c.SecretList(ctx, client.SecretListOptions{})
380 380
 	assert.NilError(t, err)
381
-	assert.Assert(t, is.DeepEqual(secretNamesFromList(entries), []string{fakeName}))
381
+	assert.Assert(t, is.DeepEqual(namesFromList(res.Items), []string{fakeName}))
382 382
 
383 383
 	// Remove based on ID prefix of the fake one should succeed
384
-	err = c.SecretRemove(ctx, fakeID[:5])
384
+	_, err = c.SecretRemove(ctx, fakeID[:5], client.SecretRemoveOptions{})
385 385
 	assert.NilError(t, err)
386
-	entries, err = c.SecretList(ctx, client.SecretListOptions{})
386
+	res, err = c.SecretList(ctx, client.SecretListOptions{})
387 387
 	assert.NilError(t, err)
388
-	assert.Assert(t, is.Equal(0, len(entries)))
388
+	assert.Assert(t, is.Equal(0, len(res.Items)))
389 389
 }
390 390
 
391
-func secretNamesFromList(entries []swarmtypes.Secret) []string {
391
+func namesFromList(entries []swarmtypes.Secret) []string {
392 392
 	var values []string
393 393
 	for _, entry := range entries {
394 394
 		values = append(values, entry.Spec.Name)
... ...
@@ -199,11 +199,13 @@ func TestCreateServiceSecretFileMode(t *testing.T) {
199 199
 	defer apiClient.Close()
200 200
 
201 201
 	secretName := "TestSecret_" + t.Name()
202
-	secretResp, err := apiClient.SecretCreate(ctx, swarmtypes.SecretSpec{
203
-		Annotations: swarmtypes.Annotations{
204
-			Name: secretName,
202
+	secretResp, err := apiClient.SecretCreate(ctx, client.SecretCreateOptions{
203
+		Spec: swarmtypes.SecretSpec{
204
+			Annotations: swarmtypes.Annotations{
205
+				Name: secretName,
206
+			},
207
+			Data: []byte("TESTSECRET"),
205 208
 		},
206
-		Data: []byte("TESTSECRET"),
207 209
 	})
208 210
 	assert.NilError(t, err)
209 211
 
... ...
@@ -242,7 +244,7 @@ func TestCreateServiceSecretFileMode(t *testing.T) {
242 242
 	assert.NilError(t, err)
243 243
 	poll.WaitOn(t, swarm.NoTasksForService(ctx, apiClient, serviceID), swarm.ServicePoll)
244 244
 
245
-	err = apiClient.SecretRemove(ctx, secretName)
245
+	_, err = apiClient.SecretRemove(ctx, secretName, client.SecretRemoveOptions{})
246 246
 	assert.NilError(t, err)
247 247
 }
248 248
 
... ...
@@ -83,11 +83,13 @@ func TestServiceUpdateSecrets(t *testing.T) {
83 83
 
84 84
 	secretName := "TestSecret_" + t.Name()
85 85
 	secretTarget := "targetName"
86
-	secretResp, err := apiClient.SecretCreate(ctx, swarmtypes.SecretSpec{
87
-		Annotations: swarmtypes.Annotations{
88
-			Name: secretName,
86
+	secretResp, err := apiClient.SecretCreate(ctx, client.SecretCreateOptions{
87
+		Spec: swarmtypes.SecretSpec{
88
+			Annotations: swarmtypes.Annotations{
89
+				Name: secretName,
90
+			},
91
+			Data: []byte("TESTINGDATA"),
89 92
 		},
90
-		Data: []byte("TESTINGDATA"),
91 93
 	})
92 94
 	assert.NilError(t, err)
93 95
 	assert.Check(t, secretResp.ID != "")
94 96
deleted file mode 100644
... ...
@@ -1,74 +0,0 @@
1
-package daemon
2
-
3
-import (
4
-	"context"
5
-	"testing"
6
-
7
-	"github.com/moby/moby/api/types/swarm"
8
-	"github.com/moby/moby/client"
9
-	"gotest.tools/v3/assert"
10
-)
11
-
12
-// SecretConstructor defines a swarm secret constructor
13
-type SecretConstructor func(*swarm.Secret)
14
-
15
-// CreateSecret creates a secret given the specified spec
16
-func (d *Daemon) CreateSecret(t testing.TB, secretSpec swarm.SecretSpec) string {
17
-	t.Helper()
18
-	cli := d.NewClientT(t)
19
-	defer cli.Close()
20
-
21
-	scr, err := cli.SecretCreate(context.Background(), secretSpec)
22
-	assert.NilError(t, err)
23
-
24
-	return scr.ID
25
-}
26
-
27
-// ListSecrets returns the list of the current swarm secrets
28
-func (d *Daemon) ListSecrets(t testing.TB) []swarm.Secret {
29
-	t.Helper()
30
-	cli := d.NewClientT(t)
31
-	defer cli.Close()
32
-
33
-	secrets, err := cli.SecretList(context.Background(), client.SecretListOptions{})
34
-	assert.NilError(t, err)
35
-	return secrets
36
-}
37
-
38
-// GetSecret returns a swarm secret identified by the specified id
39
-func (d *Daemon) GetSecret(t testing.TB, id string) *swarm.Secret {
40
-	t.Helper()
41
-	cli := d.NewClientT(t)
42
-	defer cli.Close()
43
-
44
-	secret, _, err := cli.SecretInspectWithRaw(context.Background(), id)
45
-	assert.NilError(t, err)
46
-	return &secret
47
-}
48
-
49
-// DeleteSecret removes the swarm secret identified by the specified id
50
-func (d *Daemon) DeleteSecret(t testing.TB, id string) {
51
-	t.Helper()
52
-	cli := d.NewClientT(t)
53
-	defer cli.Close()
54
-
55
-	err := cli.SecretRemove(context.Background(), id)
56
-	assert.NilError(t, err)
57
-}
58
-
59
-// UpdateSecret updates the swarm secret identified by the specified id
60
-// Currently, only label update is supported.
61
-func (d *Daemon) UpdateSecret(t testing.TB, id string, f ...SecretConstructor) {
62
-	t.Helper()
63
-	cli := d.NewClientT(t)
64
-	defer cli.Close()
65
-
66
-	secret := d.GetSecret(t, id)
67
-	for _, fn := range f {
68
-		fn(secret)
69
-	}
70
-
71
-	err := cli.SecretUpdate(context.Background(), secret.ID, secret.Version, secret.Spec)
72
-
73
-	assert.NilError(t, err)
74
-}
... ...
@@ -203,11 +203,11 @@ type VolumeAPIClient interface {
203 203
 
204 204
 // SecretAPIClient defines API client methods for secrets
205 205
 type SecretAPIClient interface {
206
-	SecretList(ctx context.Context, options SecretListOptions) ([]swarm.Secret, error)
207
-	SecretCreate(ctx context.Context, secret swarm.SecretSpec) (swarm.SecretCreateResponse, error)
208
-	SecretRemove(ctx context.Context, id string) error
209
-	SecretInspectWithRaw(ctx context.Context, name string) (swarm.Secret, []byte, error)
210
-	SecretUpdate(ctx context.Context, id string, version swarm.Version, secret swarm.SecretSpec) error
206
+	SecretList(ctx context.Context, options SecretListOptions) (SecretListResult, error)
207
+	SecretCreate(ctx context.Context, options SecretCreateOptions) (SecretCreateResult, error)
208
+	SecretRemove(ctx context.Context, id string, options SecretRemoveOptions) (SecretRemoveResult, error)
209
+	SecretInspect(ctx context.Context, id string, options SecretInspectOptions) (SecretInspectResult, error)
210
+	SecretUpdate(ctx context.Context, id string, options SecretUpdateOptions) (SecretUpdateResult, error)
211 211
 }
212 212
 
213 213
 // ConfigAPIClient defines API client methods for configs
... ...
@@ -7,15 +7,28 @@ import (
7 7
 	"github.com/moby/moby/api/types/swarm"
8 8
 )
9 9
 
10
+// SecretCreateOptions holds options for creating a secret.
11
+type SecretCreateOptions struct {
12
+	Spec swarm.SecretSpec
13
+}
14
+
15
+// SecretCreateResult holds the result from the [Client.SecretCreate] method.
16
+type SecretCreateResult struct {
17
+	ID string
18
+}
19
+
10 20
 // SecretCreate creates a new secret.
11
-func (cli *Client) SecretCreate(ctx context.Context, secret swarm.SecretSpec) (swarm.SecretCreateResponse, error) {
12
-	resp, err := cli.post(ctx, "/secrets/create", nil, secret, nil)
21
+func (cli *Client) SecretCreate(ctx context.Context, options SecretCreateOptions) (SecretCreateResult, error) {
22
+	resp, err := cli.post(ctx, "/secrets/create", nil, options.Spec, nil)
13 23
 	defer ensureReaderClosed(resp)
14 24
 	if err != nil {
15
-		return swarm.SecretCreateResponse{}, err
25
+		return SecretCreateResult{}, err
16 26
 	}
17 27
 
18
-	var response swarm.SecretCreateResponse
19
-	err = json.NewDecoder(resp.Body).Decode(&response)
20
-	return response, err
28
+	var out swarm.ConfigCreateResponse
29
+	err = json.NewDecoder(resp.Body).Decode(&out)
30
+	if err != nil {
31
+		return SecretCreateResult{}, err
32
+	}
33
+	return SecretCreateResult{ID: out.ID}, nil
21 34
 }
... ...
@@ -1,34 +1,35 @@
1 1
 package client
2 2
 
3 3
 import (
4
-	"bytes"
5 4
 	"context"
6
-	"encoding/json"
7
-	"io"
8 5
 
9 6
 	"github.com/moby/moby/api/types/swarm"
10 7
 )
11 8
 
12
-// SecretInspectWithRaw returns the secret information with raw data
13
-func (cli *Client) SecretInspectWithRaw(ctx context.Context, id string) (swarm.Secret, []byte, error) {
9
+// SecretInspectOptions holds options for inspecting a secret.
10
+type SecretInspectOptions struct {
11
+	// Add future optional parameters here
12
+}
13
+
14
+// SecretInspectResult holds the result from the [Client.SecretInspect]. method.
15
+type SecretInspectResult struct {
16
+	Secret swarm.Secret
17
+	Raw    []byte
18
+}
19
+
20
+// SecretInspect returns the secret information with raw data.
21
+func (cli *Client) SecretInspect(ctx context.Context, id string, options SecretInspectOptions) (SecretInspectResult, error) {
14 22
 	id, err := trimID("secret", id)
15 23
 	if err != nil {
16
-		return swarm.Secret{}, nil, err
24
+		return SecretInspectResult{}, err
17 25
 	}
18 26
 	resp, err := cli.get(ctx, "/secrets/"+id, nil, nil)
19 27
 	defer ensureReaderClosed(resp)
20 28
 	if err != nil {
21
-		return swarm.Secret{}, nil, err
29
+		return SecretInspectResult{}, err
22 30
 	}
23 31
 
24
-	body, err := io.ReadAll(resp.Body)
25
-	if err != nil {
26
-		return swarm.Secret{}, nil, err
27
-	}
28
-
29
-	var secret swarm.Secret
30
-	rdr := bytes.NewReader(body)
31
-	err = json.NewDecoder(rdr).Decode(&secret)
32
-
33
-	return secret, body, err
32
+	var out SecretInspectResult
33
+	out.Raw, err = decodeWithRaw(resp, &out.Secret)
34
+	return out, err
34 35
 }
... ...
@@ -8,18 +8,31 @@ import (
8 8
 	"github.com/moby/moby/api/types/swarm"
9 9
 )
10 10
 
11
+// SecretListOptions holds parameters to list secrets
12
+type SecretListOptions struct {
13
+	Filters Filters
14
+}
15
+
16
+// SecretListResult holds the result from the [client.SecretList] method.
17
+type SecretListResult struct {
18
+	Items []swarm.Secret
19
+}
20
+
11 21
 // SecretList returns the list of secrets.
12
-func (cli *Client) SecretList(ctx context.Context, options SecretListOptions) ([]swarm.Secret, error) {
22
+func (cli *Client) SecretList(ctx context.Context, options SecretListOptions) (SecretListResult, error) {
13 23
 	query := url.Values{}
14
-
15 24
 	options.Filters.updateURLValues(query)
25
+
16 26
 	resp, err := cli.get(ctx, "/secrets", query, nil)
17 27
 	defer ensureReaderClosed(resp)
18 28
 	if err != nil {
19
-		return nil, err
29
+		return SecretListResult{}, err
20 30
 	}
21 31
 
22
-	var secrets []swarm.Secret
23
-	err = json.NewDecoder(resp.Body).Decode(&secrets)
24
-	return secrets, err
32
+	var out SecretListResult
33
+	err = json.NewDecoder(resp.Body).Decode(&out.Items)
34
+	if err != nil {
35
+		return SecretListResult{}, err
36
+	}
37
+	return out, nil
25 38
 }
26 39
deleted file mode 100644
... ...
@@ -1,6 +0,0 @@
1
-package client
2
-
3
-// SecretListOptions holds parameters to list secrets
4
-type SecretListOptions struct {
5
-	Filters Filters
6
-}
... ...
@@ -2,13 +2,24 @@ package client
2 2
 
3 3
 import "context"
4 4
 
5
+type SecretRemoveOptions struct {
6
+	// Add future optional parameters here
7
+}
8
+
9
+type SecretRemoveResult struct {
10
+	// Add future fields here
11
+}
12
+
5 13
 // SecretRemove removes a secret.
6
-func (cli *Client) SecretRemove(ctx context.Context, id string) error {
14
+func (cli *Client) SecretRemove(ctx context.Context, id string, options SecretRemoveOptions) (SecretRemoveResult, error) {
7 15
 	id, err := trimID("secret", id)
8 16
 	if err != nil {
9
-		return err
17
+		return SecretRemoveResult{}, err
10 18
 	}
11 19
 	resp, err := cli.delete(ctx, "/secrets/"+id, nil, nil)
12 20
 	defer ensureReaderClosed(resp)
13
-	return err
21
+	if err != nil {
22
+		return SecretRemoveResult{}, err
23
+	}
24
+	return SecretRemoveResult{}, nil
14 25
 }
... ...
@@ -7,15 +7,26 @@ import (
7 7
 	"github.com/moby/moby/api/types/swarm"
8 8
 )
9 9
 
10
+// SecretUpdateOptions holds options for updating a secret.
11
+type SecretUpdateOptions struct {
12
+	Version swarm.Version
13
+	Spec    swarm.SecretSpec
14
+}
15
+
16
+type SecretUpdateResult struct{}
17
+
10 18
 // SecretUpdate attempts to update a secret.
11
-func (cli *Client) SecretUpdate(ctx context.Context, id string, version swarm.Version, secret swarm.SecretSpec) error {
19
+func (cli *Client) SecretUpdate(ctx context.Context, id string, options SecretUpdateOptions) (SecretUpdateResult, error) {
12 20
 	id, err := trimID("secret", id)
13 21
 	if err != nil {
14
-		return err
22
+		return SecretUpdateResult{}, err
15 23
 	}
16 24
 	query := url.Values{}
17
-	query.Set("version", version.String())
18
-	resp, err := cli.post(ctx, "/secrets/"+id+"/update", query, secret, nil)
25
+	query.Set("version", options.Version.String())
26
+	resp, err := cli.post(ctx, "/secrets/"+id+"/update", query, options.Spec, nil)
19 27
 	defer ensureReaderClosed(resp)
20
-	return err
28
+	if err != nil {
29
+		return SecretUpdateResult{}, err
30
+	}
31
+	return SecretUpdateResult{}, nil
21 32
 }