client/config: Wrap results and options

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
Paweł Gronowski
2025-10-17 09:51:02 +02:00
parent 72e9c3af99
commit f4b06e66e1
22 changed files with 278 additions and 162 deletions

View File

@@ -214,9 +214,9 @@ type SecretAPIClient interface {
// ConfigAPIClient defines API client methods for configs
type ConfigAPIClient interface {
ConfigList(ctx context.Context, options ConfigListOptions) ([]swarm.Config, error)
ConfigCreate(ctx context.Context, config swarm.ConfigSpec) (swarm.ConfigCreateResponse, error)
ConfigRemove(ctx context.Context, id string) error
ConfigInspectWithRaw(ctx context.Context, name string) (swarm.Config, []byte, error)
ConfigUpdate(ctx context.Context, id string, version swarm.Version, config swarm.ConfigSpec) error
ConfigList(ctx context.Context, options ConfigListOptions) (ConfigListResult, error)
ConfigCreate(ctx context.Context, options ConfigCreateOptions) (ConfigCreateResult, error)
ConfigRemove(ctx context.Context, id string, options ConfigRemoveOptions) (ConfigRemoveResult, error)
ConfigInspect(ctx context.Context, id string, options ConfigInspectOptions) (ConfigInspectResult, error)
ConfigUpdate(ctx context.Context, id string, options ConfigUpdateOptions) (ConfigUpdateResult, error)
}

View File

@@ -7,15 +7,28 @@ import (
"github.com/moby/moby/api/types/swarm"
)
// ConfigCreateOptions holds options for creating a config.
type ConfigCreateOptions struct {
Config swarm.ConfigSpec
}
// ConfigCreateResult holds the result from the ConfigCreate method.
type ConfigCreateResult struct {
ID string
}
// ConfigCreate creates a new config.
func (cli *Client) ConfigCreate(ctx context.Context, config swarm.ConfigSpec) (swarm.ConfigCreateResponse, error) {
resp, err := cli.post(ctx, "/configs/create", nil, config, nil)
func (cli *Client) ConfigCreate(ctx context.Context, options ConfigCreateOptions) (ConfigCreateResult, error) {
resp, err := cli.post(ctx, "/configs/create", nil, options.Config, nil)
defer ensureReaderClosed(resp)
if err != nil {
return swarm.ConfigCreateResponse{}, err
return ConfigCreateResult{}, err
}
var response swarm.ConfigCreateResponse
err = json.NewDecoder(resp.Body).Decode(&response)
return response, err
var out swarm.ConfigCreateResponse
err = json.NewDecoder(resp.Body).Decode(&out)
if err != nil {
return ConfigCreateResult{}, err
}
return ConfigCreateResult{ID: out.ID}, nil
}

View File

@@ -20,7 +20,7 @@ func TestConfigCreateError(t *testing.T) {
)
assert.NilError(t, err)
_, err = client.ConfigCreate(context.Background(), swarm.ConfigSpec{})
_, err = client.ConfigCreate(context.Background(), ConfigCreateOptions{Config: swarm.ConfigSpec{}})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
}
@@ -45,7 +45,7 @@ func TestConfigCreate(t *testing.T) {
)
assert.NilError(t, err)
r, err := client.ConfigCreate(context.Background(), swarm.ConfigSpec{})
r, err := client.ConfigCreate(context.Background(), ConfigCreateOptions{Config: swarm.ConfigSpec{}})
assert.NilError(t, err)
assert.Check(t, is.Equal(r.ID, "test_config"))
}

View File

@@ -9,26 +9,38 @@ import (
"github.com/moby/moby/api/types/swarm"
)
// ConfigInspectWithRaw returns the config information with raw data
func (cli *Client) ConfigInspectWithRaw(ctx context.Context, id string) (swarm.Config, []byte, error) {
id, err := trimID("contig", id)
// ConfigInspectOptions holds options for inspecting a config.
type ConfigInspectOptions struct {
// Add future optional parameters here
}
// ConfigInspectResult holds the result from the ConfigInspect method.
type ConfigInspectResult struct {
Config swarm.Config
Raw []byte
}
// ConfigInspect returns the config information with raw data
func (cli *Client) ConfigInspect(ctx context.Context, id string, options ConfigInspectOptions) (ConfigInspectResult, error) {
id, err := trimID("config", id)
if err != nil {
return swarm.Config{}, nil, err
return ConfigInspectResult{}, err
}
resp, err := cli.get(ctx, "/configs/"+id, nil, nil)
defer ensureReaderClosed(resp)
if err != nil {
return swarm.Config{}, nil, err
return ConfigInspectResult{}, err
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return swarm.Config{}, nil, err
return ConfigInspectResult{}, err
}
var config swarm.Config
var out ConfigInspectResult
out.Raw = body
rdr := bytes.NewReader(body)
err = json.NewDecoder(rdr).Decode(&config)
err = json.NewDecoder(rdr).Decode(&out.Config)
return config, body, err
return out, err
}

View File

@@ -21,7 +21,7 @@ func TestConfigInspectNotFound(t *testing.T) {
)
assert.NilError(t, err)
_, _, err = client.ConfigInspectWithRaw(context.Background(), "unknown")
_, err = client.ConfigInspect(context.Background(), "unknown", ConfigInspectOptions{})
assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
}
@@ -32,11 +32,11 @@ func TestConfigInspectWithEmptyID(t *testing.T) {
}),
)
assert.NilError(t, err)
_, _, err = client.ConfigInspectWithRaw(context.Background(), "")
_, err = client.ConfigInspect(context.Background(), "", ConfigInspectOptions{})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
assert.Check(t, is.ErrorContains(err, "value is empty"))
_, _, err = client.ConfigInspectWithRaw(context.Background(), " ")
_, err = client.ConfigInspect(context.Background(), " ", ConfigInspectOptions{})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
assert.Check(t, is.ErrorContains(err, "value is empty"))
}
@@ -47,7 +47,7 @@ func TestConfigInspectError(t *testing.T) {
)
assert.NilError(t, err)
_, _, err = client.ConfigInspectWithRaw(context.Background(), "nothing")
_, err = client.ConfigInspect(context.Background(), "nothing", ConfigInspectOptions{})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
}
@@ -57,7 +57,7 @@ func TestConfigInspectConfigNotFound(t *testing.T) {
)
assert.NilError(t, err)
_, _, err = client.ConfigInspectWithRaw(context.Background(), "unknown")
_, err = client.ConfigInspect(context.Background(), "unknown", ConfigInspectOptions{})
assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
}
@@ -82,7 +82,7 @@ func TestConfigInspect(t *testing.T) {
)
assert.NilError(t, err)
configInspect, _, err := client.ConfigInspectWithRaw(context.Background(), "config_id")
result, err := client.ConfigInspect(context.Background(), "config_id", ConfigInspectOptions{})
assert.NilError(t, err)
assert.Check(t, is.Equal(configInspect.ID, "config_id"))
assert.Check(t, is.Equal(result.Config.ID, "config_id"))
}

View File

@@ -8,18 +8,26 @@ import (
"github.com/moby/moby/api/types/swarm"
)
// ConfigListResult holds the result from the ConfigList method.
type ConfigListResult struct {
Configs []swarm.Config
}
// ConfigList returns the list of configs.
func (cli *Client) ConfigList(ctx context.Context, options ConfigListOptions) ([]swarm.Config, error) {
func (cli *Client) ConfigList(ctx context.Context, options ConfigListOptions) (ConfigListResult, error) {
var out ConfigListResult
query := url.Values{}
options.Filters.updateURLValues(query)
resp, err := cli.get(ctx, "/configs", query, nil)
defer ensureReaderClosed(resp)
if err != nil {
return nil, err
return ConfigListResult{}, err
}
var configs []swarm.Config
err = json.NewDecoder(resp.Body).Decode(&configs)
return configs, err
err = json.NewDecoder(resp.Body).Decode(&out.Configs)
if err != nil {
return ConfigListResult{}, err
}
return out, nil
}

View File

@@ -81,8 +81,8 @@ func TestConfigList(t *testing.T) {
)
assert.NilError(t, err)
configs, err := client.ConfigList(context.Background(), listCase.options)
result, err := client.ConfigList(context.Background(), listCase.options)
assert.NilError(t, err)
assert.Check(t, is.Len(configs, 2))
assert.Check(t, is.Len(result.Configs, 2))
}
}

View File

@@ -2,13 +2,20 @@ package client
import "context"
type ConfigRemoveOptions struct{}
type ConfigRemoveResult struct{}
// ConfigRemove removes a config.
func (cli *Client) ConfigRemove(ctx context.Context, id string) error {
func (cli *Client) ConfigRemove(ctx context.Context, id string, options ConfigRemoveOptions) (ConfigRemoveResult, error) {
id, err := trimID("config", id)
if err != nil {
return err
return ConfigRemoveResult{}, err
}
resp, err := cli.delete(ctx, "/configs/"+id, nil, nil)
defer ensureReaderClosed(resp)
return err
if err != nil {
return ConfigRemoveResult{}, err
}
return ConfigRemoveResult{}, nil
}

View File

@@ -18,14 +18,14 @@ func TestConfigRemoveError(t *testing.T) {
)
assert.NilError(t, err)
err = client.ConfigRemove(context.Background(), "config_id")
_, err = client.ConfigRemove(context.Background(), "config_id", ConfigRemoveOptions{})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
err = client.ConfigRemove(context.Background(), "")
_, err = client.ConfigRemove(context.Background(), "", ConfigRemoveOptions{})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
assert.Check(t, is.ErrorContains(err, "value is empty"))
err = client.ConfigRemove(context.Background(), " ")
_, err = client.ConfigRemove(context.Background(), " ", ConfigRemoveOptions{})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
assert.Check(t, is.ErrorContains(err, "value is empty"))
}
@@ -46,6 +46,6 @@ func TestConfigRemove(t *testing.T) {
)
assert.NilError(t, err)
err = client.ConfigRemove(context.Background(), "config_id")
_, err = client.ConfigRemove(context.Background(), "config_id", ConfigRemoveOptions{})
assert.NilError(t, err)
}

View File

@@ -7,15 +7,26 @@ import (
"github.com/moby/moby/api/types/swarm"
)
// ConfigUpdateOptions holds options for updating a config.
type ConfigUpdateOptions struct {
Version swarm.Version
Config swarm.ConfigSpec
}
type ConfigUpdateResult struct{}
// ConfigUpdate attempts to update a config
func (cli *Client) ConfigUpdate(ctx context.Context, id string, version swarm.Version, config swarm.ConfigSpec) error {
func (cli *Client) ConfigUpdate(ctx context.Context, id string, options ConfigUpdateOptions) (ConfigUpdateResult, error) {
id, err := trimID("config", id)
if err != nil {
return err
return ConfigUpdateResult{}, err
}
query := url.Values{}
query.Set("version", version.String())
resp, err := cli.post(ctx, "/configs/"+id+"/update", query, config, nil)
query.Set("version", options.Version.String())
resp, err := cli.post(ctx, "/configs/"+id+"/update", query, options.Config, nil)
defer ensureReaderClosed(resp)
return err
if err != nil {
return ConfigUpdateResult{}, err
}
return ConfigUpdateResult{}, nil
}

View File

@@ -19,14 +19,14 @@ func TestConfigUpdateError(t *testing.T) {
)
assert.NilError(t, err)
err = client.ConfigUpdate(context.Background(), "config_id", swarm.Version{}, swarm.ConfigSpec{})
_, err = client.ConfigUpdate(context.Background(), "config_id", ConfigUpdateOptions{Version: swarm.Version{}, Config: swarm.ConfigSpec{}})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
err = client.ConfigUpdate(context.Background(), "", swarm.Version{}, swarm.ConfigSpec{})
_, err = client.ConfigUpdate(context.Background(), "", ConfigUpdateOptions{Version: swarm.Version{}, Config: swarm.ConfigSpec{}})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
assert.Check(t, is.ErrorContains(err, "value is empty"))
err = client.ConfigUpdate(context.Background(), " ", swarm.Version{}, swarm.ConfigSpec{})
_, err = client.ConfigUpdate(context.Background(), " ", ConfigUpdateOptions{Version: swarm.Version{}, Config: swarm.ConfigSpec{}})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
assert.Check(t, is.ErrorContains(err, "value is empty"))
}
@@ -47,6 +47,6 @@ func TestConfigUpdate(t *testing.T) {
)
assert.NilError(t, err)
err = client.ConfigUpdate(context.Background(), "config_id", swarm.Version{}, swarm.ConfigSpec{})
_, err = client.ConfigUpdate(context.Background(), "config_id", ConfigUpdateOptions{Version: swarm.Version{}, Config: swarm.ConfigSpec{}})
assert.NilError(t, err)
}

View File

@@ -33,14 +33,14 @@ func TestConfigInspect(t *testing.T) {
testName := t.Name()
configID := createConfig(ctx, t, c, testName, []byte("TESTINGDATA"), nil)
insp, body, err := c.ConfigInspectWithRaw(ctx, configID)
result, err := c.ConfigInspect(ctx, configID, client.ConfigInspectOptions{})
assert.NilError(t, err)
assert.Check(t, is.Equal(insp.Spec.Name, testName))
assert.Check(t, is.Equal(result.Config.Spec.Name, testName))
var config swarmtypes.Config
err = json.Unmarshal(body, &config)
err = json.Unmarshal(result.Raw, &config)
assert.NilError(t, err)
assert.Check(t, is.DeepEqual(config, insp))
assert.Check(t, is.DeepEqual(config, result.Config))
}
func TestConfigList(t *testing.T) {
@@ -54,9 +54,9 @@ func TestConfigList(t *testing.T) {
defer c.Close()
// This test case is ported from the original TestConfigsEmptyList
configs, err := c.ConfigList(ctx, client.ConfigListOptions{})
result, err := c.ConfigList(ctx, client.ConfigListOptions{})
assert.NilError(t, err)
assert.Check(t, is.Equal(len(configs), 0))
assert.Check(t, is.Equal(len(result.Configs), 0))
testName0 := "test0-" + t.Name()
testName1 := "test1-" + t.Name()
@@ -71,7 +71,7 @@ func TestConfigList(t *testing.T) {
// test by `config ls`
entries, err := c.ConfigList(ctx, client.ConfigListOptions{})
assert.NilError(t, err)
assert.Check(t, is.DeepEqual(configNamesFromList(entries), testNames))
assert.Check(t, is.DeepEqual(configNamesFromList(entries.Configs), testNames))
testCases := []struct {
desc string
@@ -111,22 +111,24 @@ func TestConfigList(t *testing.T) {
Filters: tc.filters,
})
assert.NilError(t, err)
assert.Check(t, is.DeepEqual(configNamesFromList(entries), tc.expected))
assert.Check(t, is.DeepEqual(configNamesFromList(entries.Configs), tc.expected))
})
}
}
func createConfig(ctx context.Context, t *testing.T, client client.APIClient, name string, data []byte, labels map[string]string) string {
config, err := client.ConfigCreate(ctx, swarmtypes.ConfigSpec{
Annotations: swarmtypes.Annotations{
Name: name,
Labels: labels,
func createConfig(ctx context.Context, t *testing.T, apiClient client.APIClient, name string, data []byte, labels map[string]string) string {
result, err := apiClient.ConfigCreate(ctx, client.ConfigCreateOptions{
Config: swarmtypes.ConfigSpec{
Annotations: swarmtypes.Annotations{
Name: name,
Labels: labels,
},
Data: data,
},
Data: data,
})
assert.NilError(t, err)
assert.Check(t, config.ID != "")
return config.ID
assert.Check(t, result.Response.ID != "")
return result.Response.ID
}
func TestConfigsCreateAndDelete(t *testing.T) {
@@ -141,14 +143,14 @@ func TestConfigsCreateAndDelete(t *testing.T) {
testName := "test_config-" + t.Name()
configID := createConfig(ctx, t, c, testName, []byte("TESTINGDATA"), nil)
err := c.ConfigRemove(ctx, configID)
_, err := c.ConfigRemove(ctx, configID, client.ConfigRemoveOptions{})
assert.NilError(t, err)
_, _, err = c.ConfigInspectWithRaw(ctx, configID)
_, err = c.ConfigInspect(ctx, configID, client.ConfigInspectOptions{})
assert.Check(t, cerrdefs.IsNotFound(err))
assert.Check(t, is.ErrorContains(err, configID))
err = c.ConfigRemove(ctx, "non-existing")
_, err = c.ConfigRemove(ctx, "non-existing", client.ConfigRemoveOptions{})
assert.Check(t, cerrdefs.IsNotFound(err))
assert.Check(t, is.ErrorContains(err, "non-existing"))
@@ -158,12 +160,12 @@ func TestConfigsCreateAndDelete(t *testing.T) {
"key2": "value2",
})
insp, _, err := c.ConfigInspectWithRaw(ctx, configID)
result, err := c.ConfigInspect(ctx, configID, client.ConfigInspectOptions{})
assert.NilError(t, err)
assert.Check(t, is.Equal(insp.Spec.Name, testName))
assert.Check(t, is.Equal(len(insp.Spec.Labels), 2))
assert.Check(t, is.Equal(insp.Spec.Labels["key1"], "value1"))
assert.Check(t, is.Equal(insp.Spec.Labels["key2"], "value2"))
assert.Check(t, is.Equal(result.Config.Spec.Name, testName))
assert.Check(t, is.Equal(len(result.Config.Spec.Labels), 2))
assert.Check(t, is.Equal(result.Config.Spec.Labels["key1"], "value1"))
assert.Check(t, is.Equal(result.Config.Spec.Labels["key2"], "value2"))
}
func TestConfigsUpdate(t *testing.T) {
@@ -179,41 +181,41 @@ func TestConfigsUpdate(t *testing.T) {
testName := "test_config-" + t.Name()
configID := createConfig(ctx, t, c, testName, []byte("TESTINGDATA"), nil)
insp, _, err := c.ConfigInspectWithRaw(ctx, configID)
insp, err := c.ConfigInspect(ctx, configID, client.ConfigInspectOptions{})
assert.NilError(t, err)
assert.Check(t, is.Equal(insp.ID, configID))
assert.Check(t, is.Equal(insp.Config.ID, configID))
// test UpdateConfig with full ID
insp.Spec.Labels = map[string]string{"test": "test1"}
err = c.ConfigUpdate(ctx, configID, insp.Version, insp.Spec)
insp.Config.Spec.Labels = map[string]string{"test": "test1"}
_, err = c.ConfigUpdate(ctx, client.SwarmVersionedID{ID: configID, Version: insp.Config.Version}, client.ConfigUpdateOptions{Config: insp.Config.Spec})
assert.NilError(t, err)
insp, _, err = c.ConfigInspectWithRaw(ctx, configID)
insp, err = c.ConfigInspect(ctx, configID, client.ConfigInspectOptions{})
assert.NilError(t, err)
assert.Check(t, is.Equal(insp.Spec.Labels["test"], "test1"))
assert.Check(t, is.Equal(insp.Config.Spec.Labels["test"], "test1"))
// test UpdateConfig with full name
insp.Spec.Labels = map[string]string{"test": "test2"}
err = c.ConfigUpdate(ctx, testName, insp.Version, insp.Spec)
insp.Config.Spec.Labels = map[string]string{"test": "test2"}
_, err = c.ConfigUpdate(ctx, client.SwarmVersionedID{ID: testName, Version: insp.Config.Version}, client.ConfigUpdateOptions{Config: insp.Config.Spec})
assert.NilError(t, err)
insp, _, err = c.ConfigInspectWithRaw(ctx, configID)
insp, err = c.ConfigInspect(ctx, configID, client.ConfigInspectOptions{})
assert.NilError(t, err)
assert.Check(t, is.Equal(insp.Spec.Labels["test"], "test2"))
assert.Check(t, is.Equal(insp.Config.Spec.Labels["test"], "test2"))
// test UpdateConfig with prefix ID
insp.Spec.Labels = map[string]string{"test": "test3"}
err = c.ConfigUpdate(ctx, configID[:1], insp.Version, insp.Spec)
insp.Config.Spec.Labels = map[string]string{"test": "test3"}
_, err = c.ConfigUpdate(ctx, client.SwarmVersionedID{ID: configID[:1], Version: insp.Config.Version}, client.ConfigUpdateOptions{Config: insp.Config.Spec})
assert.NilError(t, err)
insp, _, err = c.ConfigInspectWithRaw(ctx, configID)
insp, err = c.ConfigInspect(ctx, configID, client.ConfigInspectOptions{})
assert.NilError(t, err)
assert.Check(t, is.Equal(insp.Spec.Labels["test"], "test3"))
assert.Check(t, is.Equal(insp.Config.Spec.Labels["test"], "test3"))
// test UpdateConfig in updating Data which is not supported in daemon
// this test will produce an error in func UpdateConfig
insp.Spec.Data = []byte("TESTINGDATA2")
err = c.ConfigUpdate(ctx, configID, insp.Version, insp.Spec)
insp.Config.Spec.Data = []byte("TESTINGDATA2")
_, err = c.ConfigUpdate(ctx, client.SwarmVersionedID{ID: configID, Version: insp.Config.Version}, client.ConfigUpdateOptions{Config: insp.Config.Spec})
assert.Check(t, cerrdefs.IsInvalidArgument(err))
assert.Check(t, is.ErrorContains(err, "only updates to Labels are allowed"))
}
@@ -244,7 +246,9 @@ func TestTemplatedConfig(t *testing.T) {
},
Data: []byte("this is a config"),
}
referencedConfig, err := c.ConfigCreate(ctx, referencedConfigSpec)
referencedConfigResult, err := c.ConfigCreate(ctx, client.ConfigCreateOptions{
Config: referencedConfigSpec,
})
assert.Check(t, err)
templatedConfigName := "templated_config-" + t.Name()
@@ -261,7 +265,9 @@ func TestTemplatedConfig(t *testing.T) {
`),
}
templatedConfig, err := c.ConfigCreate(ctx, configSpec)
templatedConfigResult, err := c.ConfigCreate(ctx, client.ConfigCreateOptions{
Config: configSpec,
})
assert.Check(t, err)
const serviceName = "svc_templated_config"
@@ -274,7 +280,7 @@ func TestTemplatedConfig(t *testing.T) {
GID: "0",
Mode: 0o600,
},
ConfigID: templatedConfig.ID,
ConfigID: templatedConfigResult.Response.ID,
ConfigName: templatedConfigName,
},
),
@@ -286,7 +292,7 @@ func TestTemplatedConfig(t *testing.T) {
GID: "0",
Mode: 0o600,
},
ConfigID: referencedConfig.ID,
ConfigID: referencedConfigResult.Response.ID,
ConfigName: referencedConfigName,
},
),
@@ -356,16 +362,16 @@ func TestConfigCreateResolve(t *testing.T) {
entries, err := c.ConfigList(ctx, client.ConfigListOptions{})
assert.NilError(t, err)
assert.Assert(t, is.Contains(configNamesFromList(entries), configName))
assert.Assert(t, is.Contains(configNamesFromList(entries), fakeName))
assert.Assert(t, is.Contains(configNamesFromList(entries.Configs), configName))
assert.Assert(t, is.Contains(configNamesFromList(entries.Configs), fakeName))
err = c.ConfigRemove(ctx, configID)
_, err = c.ConfigRemove(ctx, configID, client.ConfigRemoveOptions{})
assert.NilError(t, err)
// Fake one will remain
entries, err = c.ConfigList(ctx, client.ConfigListOptions{})
assert.NilError(t, err)
assert.Assert(t, is.DeepEqual(configNamesFromList(entries), []string{fakeName}))
assert.Assert(t, is.DeepEqual(configNamesFromList(entries.Configs), []string{fakeName}))
// Remove based on name prefix of the fake one
// (which is the same as the ID of foo one) should not work
@@ -373,18 +379,18 @@ func TestConfigCreateResolve(t *testing.T) {
// - Full ID
// - Full Name
// - Partial ID (prefix)
err = c.ConfigRemove(ctx, configID[:5])
_, err = c.ConfigRemove(ctx, configID[:5], client.ConfigRemoveOptions{})
assert.Assert(t, err != nil)
entries, err = c.ConfigList(ctx, client.ConfigListOptions{})
assert.NilError(t, err)
assert.Assert(t, is.DeepEqual(configNamesFromList(entries), []string{fakeName}))
assert.Assert(t, is.DeepEqual(configNamesFromList(entries.Configs), []string{fakeName}))
// Remove based on ID prefix of the fake one should succeed
err = c.ConfigRemove(ctx, fakeID[:5])
_, err = c.ConfigRemove(ctx, fakeID[:5], client.ConfigRemoveOptions{})
assert.NilError(t, err)
entries, err = c.ConfigList(ctx, client.ConfigListOptions{})
assert.NilError(t, err)
assert.Assert(t, is.Equal(0, len(entries)))
assert.Assert(t, is.Equal(0, len(entries.Configs)))
}
func configNamesFromList(entries []swarmtypes.Config) []string {

View File

@@ -246,7 +246,9 @@ func TestTemplatedSecret(t *testing.T) {
},
Data: []byte("this is a config"),
}
referencedConfig, err := c.ConfigCreate(ctx, referencedConfigSpec)
referencedConfigResult, err := c.ConfigCreate(ctx, client.ConfigCreateOptions{
Config: referencedConfigSpec,
})
assert.Check(t, err)
templatedSecretName := "templated_secret_" + t.Name()
@@ -287,7 +289,7 @@ func TestTemplatedSecret(t *testing.T) {
GID: "0",
Mode: 0o600,
},
ConfigID: referencedConfig.ID,
ConfigID: referencedConfigResult.Response.ID,
ConfigName: referencedConfigName,
},
),

View File

@@ -256,11 +256,13 @@ func TestCreateServiceConfigFileMode(t *testing.T) {
defer apiClient.Close()
configName := "TestConfig_" + t.Name()
configResp, err := apiClient.ConfigCreate(ctx, swarmtypes.ConfigSpec{
Annotations: swarmtypes.Annotations{
Name: configName,
configResp, err := apiClient.ConfigCreate(ctx, client.ConfigCreateOptions{
Config: swarmtypes.ConfigSpec{
Annotations: swarmtypes.Annotations{
Name: configName,
},
Data: []byte("TESTCONFIG"),
},
Data: []byte("TESTCONFIG"),
})
assert.NilError(t, err)
@@ -277,7 +279,7 @@ func TestCreateServiceConfigFileMode(t *testing.T) {
GID: "0",
Mode: 0o777,
},
ConfigID: configResp.ID,
ConfigID: configResp.Response.ID,
ConfigName: configName,
}),
)
@@ -299,7 +301,7 @@ func TestCreateServiceConfigFileMode(t *testing.T) {
assert.NilError(t, err)
poll.WaitOn(t, swarm.NoTasksForService(ctx, apiClient, serviceID))
err = apiClient.ConfigRemove(ctx, configName)
_, err = apiClient.ConfigRemove(ctx, configName, client.ConfigRemoveOptions{})
assert.NilError(t, err)
}

View File

@@ -83,14 +83,14 @@ func TestServiceUpdateSecrets(t *testing.T) {
secretName := "TestSecret_" + t.Name()
secretTarget := "targetName"
resp, err := apiClient.SecretCreate(ctx, swarmtypes.SecretSpec{
secretResp, err := apiClient.SecretCreate(ctx, swarmtypes.SecretSpec{
Annotations: swarmtypes.Annotations{
Name: secretName,
},
Data: []byte("TESTINGDATA"),
})
assert.NilError(t, err)
assert.Check(t, resp.ID != "")
assert.Check(t, secretResp.ID != "")
serviceName := "TestService_" + t.Name()
serviceID := swarm.CreateService(ctx, t, d, swarm.ServiceWithName(serviceName))
@@ -105,7 +105,7 @@ func TestServiceUpdateSecrets(t *testing.T) {
GID: "0",
Mode: 0o600,
},
SecretID: resp.ID,
SecretID: secretResp.ID,
SecretName: secretName,
},
)
@@ -145,14 +145,16 @@ func TestServiceUpdateConfigs(t *testing.T) {
configName := "TestConfig_" + t.Name()
configTarget := "targetName"
resp, err := apiClient.ConfigCreate(ctx, swarmtypes.ConfigSpec{
Annotations: swarmtypes.Annotations{
Name: configName,
configResp, err := apiClient.ConfigCreate(ctx, client.ConfigCreateOptions{
Config: swarmtypes.ConfigSpec{
Annotations: swarmtypes.Annotations{
Name: configName,
},
Data: []byte("TESTINGDATA"),
},
Data: []byte("TESTINGDATA"),
})
assert.NilError(t, err)
assert.Check(t, resp.ID != "")
assert.Check(t, configResp.Response.ID != "")
serviceName := "TestService_" + t.Name()
serviceID := swarm.CreateService(ctx, t, d, swarm.ServiceWithName(serviceName))
@@ -167,7 +169,7 @@ func TestServiceUpdateConfigs(t *testing.T) {
GID: "0",
Mode: 0o600,
},
ConfigID: resp.ID,
ConfigID: configResp.Response.ID,
ConfigName: configName,
},
)

View File

@@ -18,9 +18,11 @@ func (d *Daemon) CreateConfig(t testing.TB, configSpec swarm.ConfigSpec) string
cli := d.NewClientT(t)
defer cli.Close()
scr, err := cli.ConfigCreate(context.Background(), configSpec)
result, err := cli.ConfigCreate(context.Background(), client.ConfigCreateOptions{
Config: configSpec,
})
assert.NilError(t, err)
return scr.ID
return result.Response.ID
}
// ListConfigs returns the list of the current swarm configs
@@ -29,9 +31,9 @@ func (d *Daemon) ListConfigs(t testing.TB) []swarm.Config {
cli := d.NewClientT(t)
defer cli.Close()
configs, err := cli.ConfigList(context.Background(), client.ConfigListOptions{})
result, err := cli.ConfigList(context.Background(), client.ConfigListOptions{})
assert.NilError(t, err)
return configs
return result.Configs
}
// GetConfig returns a swarm config identified by the specified id
@@ -40,9 +42,9 @@ func (d *Daemon) GetConfig(t testing.TB, id string) *swarm.Config {
cli := d.NewClientT(t)
defer cli.Close()
config, _, err := cli.ConfigInspectWithRaw(context.Background(), id)
result, err := cli.ConfigInspect(context.Background(), id, client.ConfigInspectOptions{})
assert.NilError(t, err)
return &config
return &result.Config
}
// DeleteConfig removes the swarm config identified by the specified id
@@ -51,7 +53,7 @@ func (d *Daemon) DeleteConfig(t testing.TB, id string) {
cli := d.NewClientT(t)
defer cli.Close()
err := cli.ConfigRemove(context.Background(), id)
_, err := cli.ConfigRemove(context.Background(), id, client.ConfigRemoveOptions{})
assert.NilError(t, err)
}
@@ -67,6 +69,6 @@ func (d *Daemon) UpdateConfig(t testing.TB, id string, f ...ConfigConstructor) {
fn(config)
}
err := cli.ConfigUpdate(context.Background(), config.ID, config.Version, config.Spec)
_, err := cli.ConfigUpdate(context.Background(), client.SwarmVersionedID{ID: config.ID, Version: config.Version}, client.ConfigUpdateOptions{Config: config.Spec})
assert.NilError(t, err)
}

View File

@@ -214,9 +214,9 @@ type SecretAPIClient interface {
// ConfigAPIClient defines API client methods for configs
type ConfigAPIClient interface {
ConfigList(ctx context.Context, options ConfigListOptions) ([]swarm.Config, error)
ConfigCreate(ctx context.Context, config swarm.ConfigSpec) (swarm.ConfigCreateResponse, error)
ConfigRemove(ctx context.Context, id string) error
ConfigInspectWithRaw(ctx context.Context, name string) (swarm.Config, []byte, error)
ConfigUpdate(ctx context.Context, id string, version swarm.Version, config swarm.ConfigSpec) error
ConfigList(ctx context.Context, options ConfigListOptions) (ConfigListResult, error)
ConfigCreate(ctx context.Context, options ConfigCreateOptions) (ConfigCreateResult, error)
ConfigRemove(ctx context.Context, id string, options ConfigRemoveOptions) (ConfigRemoveResult, error)
ConfigInspect(ctx context.Context, id string, options ConfigInspectOptions) (ConfigInspectResult, error)
ConfigUpdate(ctx context.Context, id string, options ConfigUpdateOptions) (ConfigUpdateResult, error)
}

View File

@@ -7,15 +7,28 @@ import (
"github.com/moby/moby/api/types/swarm"
)
// ConfigCreateOptions holds options for creating a config.
type ConfigCreateOptions struct {
Config swarm.ConfigSpec
}
// ConfigCreateResult holds the result from the ConfigCreate method.
type ConfigCreateResult struct {
ID string
}
// ConfigCreate creates a new config.
func (cli *Client) ConfigCreate(ctx context.Context, config swarm.ConfigSpec) (swarm.ConfigCreateResponse, error) {
resp, err := cli.post(ctx, "/configs/create", nil, config, nil)
func (cli *Client) ConfigCreate(ctx context.Context, options ConfigCreateOptions) (ConfigCreateResult, error) {
resp, err := cli.post(ctx, "/configs/create", nil, options.Config, nil)
defer ensureReaderClosed(resp)
if err != nil {
return swarm.ConfigCreateResponse{}, err
return ConfigCreateResult{}, err
}
var response swarm.ConfigCreateResponse
err = json.NewDecoder(resp.Body).Decode(&response)
return response, err
var out swarm.ConfigCreateResponse
err = json.NewDecoder(resp.Body).Decode(&out)
if err != nil {
return ConfigCreateResult{}, err
}
return ConfigCreateResult{ID: out.ID}, nil
}

View File

@@ -9,26 +9,38 @@ import (
"github.com/moby/moby/api/types/swarm"
)
// ConfigInspectWithRaw returns the config information with raw data
func (cli *Client) ConfigInspectWithRaw(ctx context.Context, id string) (swarm.Config, []byte, error) {
id, err := trimID("contig", id)
// ConfigInspectOptions holds options for inspecting a config.
type ConfigInspectOptions struct {
// Add future optional parameters here
}
// ConfigInspectResult holds the result from the ConfigInspect method.
type ConfigInspectResult struct {
Config swarm.Config
Raw []byte
}
// ConfigInspect returns the config information with raw data
func (cli *Client) ConfigInspect(ctx context.Context, id string, options ConfigInspectOptions) (ConfigInspectResult, error) {
id, err := trimID("config", id)
if err != nil {
return swarm.Config{}, nil, err
return ConfigInspectResult{}, err
}
resp, err := cli.get(ctx, "/configs/"+id, nil, nil)
defer ensureReaderClosed(resp)
if err != nil {
return swarm.Config{}, nil, err
return ConfigInspectResult{}, err
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return swarm.Config{}, nil, err
return ConfigInspectResult{}, err
}
var config swarm.Config
var out ConfigInspectResult
out.Raw = body
rdr := bytes.NewReader(body)
err = json.NewDecoder(rdr).Decode(&config)
err = json.NewDecoder(rdr).Decode(&out.Config)
return config, body, err
return out, err
}

View File

@@ -8,18 +8,26 @@ import (
"github.com/moby/moby/api/types/swarm"
)
// ConfigListResult holds the result from the ConfigList method.
type ConfigListResult struct {
Configs []swarm.Config
}
// ConfigList returns the list of configs.
func (cli *Client) ConfigList(ctx context.Context, options ConfigListOptions) ([]swarm.Config, error) {
func (cli *Client) ConfigList(ctx context.Context, options ConfigListOptions) (ConfigListResult, error) {
var out ConfigListResult
query := url.Values{}
options.Filters.updateURLValues(query)
resp, err := cli.get(ctx, "/configs", query, nil)
defer ensureReaderClosed(resp)
if err != nil {
return nil, err
return ConfigListResult{}, err
}
var configs []swarm.Config
err = json.NewDecoder(resp.Body).Decode(&configs)
return configs, err
err = json.NewDecoder(resp.Body).Decode(&out.Configs)
if err != nil {
return ConfigListResult{}, err
}
return out, nil
}

View File

@@ -2,13 +2,20 @@ package client
import "context"
type ConfigRemoveOptions struct{}
type ConfigRemoveResult struct{}
// ConfigRemove removes a config.
func (cli *Client) ConfigRemove(ctx context.Context, id string) error {
func (cli *Client) ConfigRemove(ctx context.Context, id string, options ConfigRemoveOptions) (ConfigRemoveResult, error) {
id, err := trimID("config", id)
if err != nil {
return err
return ConfigRemoveResult{}, err
}
resp, err := cli.delete(ctx, "/configs/"+id, nil, nil)
defer ensureReaderClosed(resp)
return err
if err != nil {
return ConfigRemoveResult{}, err
}
return ConfigRemoveResult{}, nil
}

View File

@@ -7,15 +7,26 @@ import (
"github.com/moby/moby/api/types/swarm"
)
// ConfigUpdateOptions holds options for updating a config.
type ConfigUpdateOptions struct {
Version swarm.Version
Config swarm.ConfigSpec
}
type ConfigUpdateResult struct{}
// ConfigUpdate attempts to update a config
func (cli *Client) ConfigUpdate(ctx context.Context, id string, version swarm.Version, config swarm.ConfigSpec) error {
func (cli *Client) ConfigUpdate(ctx context.Context, id string, options ConfigUpdateOptions) (ConfigUpdateResult, error) {
id, err := trimID("config", id)
if err != nil {
return err
return ConfigUpdateResult{}, err
}
query := url.Values{}
query.Set("version", version.String())
resp, err := cli.post(ctx, "/configs/"+id+"/update", query, config, nil)
query.Set("version", options.Version.String())
resp, err := cli.post(ctx, "/configs/"+id+"/update", query, options.Config, nil)
defer ensureReaderClosed(resp)
return err
if err != nil {
return ConfigUpdateResult{}, err
}
return ConfigUpdateResult{}, nil
}