mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Add config APIs
Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
This commit is contained in:
parent
e3a30ffca6
commit
7728557687
12 changed files with 462 additions and 0 deletions
|
|
@ -16,21 +16,32 @@ type Backend interface {
|
|||
Update(uint64, types.Spec, types.UpdateFlags) error
|
||||
GetUnlockKey() (string, error)
|
||||
UnlockSwarm(req types.UnlockRequest) error
|
||||
|
||||
GetServices(basictypes.ServiceListOptions) ([]types.Service, error)
|
||||
GetService(idOrName string, insertDefaults bool) (types.Service, error)
|
||||
CreateService(types.ServiceSpec, string) (*basictypes.ServiceCreateResponse, error)
|
||||
UpdateService(string, uint64, types.ServiceSpec, basictypes.ServiceUpdateOptions) (*basictypes.ServiceUpdateResponse, error)
|
||||
RemoveService(string) error
|
||||
|
||||
ServiceLogs(context.Context, *backend.LogSelector, *basictypes.ContainerLogsOptions) (<-chan *backend.LogMessage, error)
|
||||
|
||||
GetNodes(basictypes.NodeListOptions) ([]types.Node, error)
|
||||
GetNode(string) (types.Node, error)
|
||||
UpdateNode(string, uint64, types.NodeSpec) error
|
||||
RemoveNode(string, bool) error
|
||||
|
||||
GetTasks(basictypes.TaskListOptions) ([]types.Task, error)
|
||||
GetTask(string) (types.Task, error)
|
||||
|
||||
GetSecrets(opts basictypes.SecretListOptions) ([]types.Secret, error)
|
||||
CreateSecret(s types.SecretSpec) (string, error)
|
||||
RemoveSecret(idOrName string) error
|
||||
GetSecret(id string) (types.Secret, error)
|
||||
UpdateSecret(idOrName string, version uint64, spec types.SecretSpec) error
|
||||
|
||||
GetConfigs(opts basictypes.ConfigListOptions) ([]types.Config, error)
|
||||
CreateConfig(s types.ConfigSpec) (string, error)
|
||||
RemoveConfig(id string) error
|
||||
GetConfig(id string) (types.Config, error)
|
||||
UpdateConfig(idOrName string, version uint64, spec types.ConfigSpec) error
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,23 +31,33 @@ func (sr *swarmRouter) initRoutes() {
|
|||
router.NewGetRoute("/swarm/unlockkey", sr.getUnlockKey),
|
||||
router.NewPostRoute("/swarm/update", sr.updateCluster),
|
||||
router.NewPostRoute("/swarm/unlock", sr.unlockCluster),
|
||||
|
||||
router.NewGetRoute("/services", sr.getServices),
|
||||
router.NewGetRoute("/services/{id}", sr.getService),
|
||||
router.NewPostRoute("/services/create", sr.createService),
|
||||
router.NewPostRoute("/services/{id}/update", sr.updateService),
|
||||
router.NewDeleteRoute("/services/{id}", sr.removeService),
|
||||
router.NewGetRoute("/services/{id}/logs", sr.getServiceLogs, router.WithCancel),
|
||||
|
||||
router.NewGetRoute("/nodes", sr.getNodes),
|
||||
router.NewGetRoute("/nodes/{id}", sr.getNode),
|
||||
router.NewDeleteRoute("/nodes/{id}", sr.removeNode),
|
||||
router.NewPostRoute("/nodes/{id}/update", sr.updateNode),
|
||||
|
||||
router.NewGetRoute("/tasks", sr.getTasks),
|
||||
router.NewGetRoute("/tasks/{id}", sr.getTask),
|
||||
router.NewGetRoute("/tasks/{id}/logs", sr.getTaskLogs, router.WithCancel),
|
||||
|
||||
router.NewGetRoute("/secrets", sr.getSecrets),
|
||||
router.NewPostRoute("/secrets/create", sr.createSecret),
|
||||
router.NewDeleteRoute("/secrets/{id}", sr.removeSecret),
|
||||
router.NewGetRoute("/secrets/{id}", sr.getSecret),
|
||||
router.NewPostRoute("/secrets/{id}/update", sr.updateSecret),
|
||||
|
||||
router.NewGetRoute("/configs", sr.getConfigs),
|
||||
router.NewPostRoute("/configs/create", sr.createConfig),
|
||||
router.NewDeleteRoute("/configs/{id}", sr.removeConfig),
|
||||
router.NewGetRoute("/configs/{id}", sr.getConfig),
|
||||
router.NewPostRoute("/configs/{id}/update", sr.updateConfig),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -408,3 +408,74 @@ func (sr *swarmRouter) updateSecret(ctx context.Context, w http.ResponseWriter,
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sr *swarmRouter) getConfigs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
filters, err := filters.FromParam(r.Form.Get("filters"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configs, err := sr.backend.GetConfigs(basictypes.ConfigListOptions{Filters: filters})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return httputils.WriteJSON(w, http.StatusOK, configs)
|
||||
}
|
||||
|
||||
func (sr *swarmRouter) createConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var config types.ConfigSpec
|
||||
if err := json.NewDecoder(r.Body).Decode(&config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
id, err := sr.backend.CreateConfig(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return httputils.WriteJSON(w, http.StatusCreated, &basictypes.ConfigCreateResponse{
|
||||
ID: id,
|
||||
})
|
||||
}
|
||||
|
||||
func (sr *swarmRouter) removeConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := sr.backend.RemoveConfig(vars["id"]); err != nil {
|
||||
return err
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sr *swarmRouter) getConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
config, err := sr.backend.GetConfig(vars["id"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return httputils.WriteJSON(w, http.StatusOK, config)
|
||||
}
|
||||
|
||||
func (sr *swarmRouter) updateConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var config types.ConfigSpec
|
||||
if err := json.NewDecoder(r.Body).Decode(&config); err != nil {
|
||||
return errors.NewBadRequestError(err)
|
||||
}
|
||||
|
||||
rawVersion := r.URL.Query().Get("version")
|
||||
version, err := strconv.ParseUint(rawVersion, 10, 64)
|
||||
if err != nil {
|
||||
return errors.NewBadRequestError(fmt.Errorf("invalid config version"))
|
||||
}
|
||||
|
||||
id := vars["id"]
|
||||
if err := sr.backend.UpdateConfig(id, version, config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
31
api/types/swarm/config.go
Normal file
31
api/types/swarm/config.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package swarm
|
||||
|
||||
import "os"
|
||||
|
||||
// Config represents a config.
|
||||
type Config struct {
|
||||
ID string
|
||||
Meta
|
||||
Spec ConfigSpec
|
||||
}
|
||||
|
||||
// ConfigSpec represents a config specification from a config in swarm
|
||||
type ConfigSpec struct {
|
||||
Annotations
|
||||
Data []byte `json:",omitempty"`
|
||||
}
|
||||
|
||||
// ConfigReferenceFileTarget is a file target in a config reference
|
||||
type ConfigReferenceFileTarget struct {
|
||||
Name string
|
||||
UID string
|
||||
GID string
|
||||
Mode os.FileMode
|
||||
}
|
||||
|
||||
// ConfigReference is a reference to a config in swarm
|
||||
type ConfigReference struct {
|
||||
File *ConfigReferenceFileTarget
|
||||
ConfigID string
|
||||
ConfigName string
|
||||
}
|
||||
|
|
@ -68,4 +68,5 @@ type ContainerSpec struct {
|
|||
Hosts []string `json:",omitempty"`
|
||||
DNSConfig *DNSConfig `json:",omitempty"`
|
||||
Secrets []*SecretReference `json:",omitempty"`
|
||||
Configs []*ConfigReference `json:",omitempty"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -522,6 +522,18 @@ type SecretListOptions struct {
|
|||
Filters filters.Args
|
||||
}
|
||||
|
||||
// ConfigCreateResponse contains the information returned to a client
|
||||
// on the creation of a new config.
|
||||
type ConfigCreateResponse struct {
|
||||
// ID is the id of the created config.
|
||||
ID string
|
||||
}
|
||||
|
||||
// ConfigListOptions holds parameters to list configs
|
||||
type ConfigListOptions struct {
|
||||
Filters filters.Args
|
||||
}
|
||||
|
||||
// PushResult contains the tag, manifest digest, and manifest size from the
|
||||
// push. It's used to signal this information to the trust code in the client
|
||||
// so it can sign the manifest if necessary.
|
||||
|
|
|
|||
117
daemon/cluster/configs.go
Normal file
117
daemon/cluster/configs.go
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
package cluster
|
||||
|
||||
import (
|
||||
apitypes "github.com/docker/docker/api/types"
|
||||
types "github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/daemon/cluster/convert"
|
||||
swarmapi "github.com/docker/swarmkit/api"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// GetConfig returns a config from a managed swarm cluster
|
||||
func (c *Cluster) GetConfig(input string) (types.Config, error) {
|
||||
var config *swarmapi.Config
|
||||
|
||||
if err := c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
|
||||
s, err := getConfig(ctx, state.controlClient, input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config = s
|
||||
return nil
|
||||
}); err != nil {
|
||||
return types.Config{}, err
|
||||
}
|
||||
return convert.ConfigFromGRPC(config), nil
|
||||
}
|
||||
|
||||
// GetConfigs returns all configs of a managed swarm cluster.
|
||||
func (c *Cluster) GetConfigs(options apitypes.ConfigListOptions) ([]types.Config, error) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
state := c.currentNodeState()
|
||||
if !state.IsActiveManager() {
|
||||
return nil, c.errNoManager(state)
|
||||
}
|
||||
|
||||
filters, err := newListConfigsFilters(options.Filters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx, cancel := c.getRequestContext()
|
||||
defer cancel()
|
||||
|
||||
r, err := state.controlClient.ListConfigs(ctx,
|
||||
&swarmapi.ListConfigsRequest{Filters: filters})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
configs := []types.Config{}
|
||||
|
||||
for _, config := range r.Configs {
|
||||
configs = append(configs, convert.ConfigFromGRPC(config))
|
||||
}
|
||||
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
// CreateConfig creates a new config in a managed swarm cluster.
|
||||
func (c *Cluster) CreateConfig(s types.ConfigSpec) (string, error) {
|
||||
var resp *swarmapi.CreateConfigResponse
|
||||
if err := c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
|
||||
configSpec := convert.ConfigSpecToGRPC(s)
|
||||
|
||||
r, err := state.controlClient.CreateConfig(ctx,
|
||||
&swarmapi.CreateConfigRequest{Spec: &configSpec})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp = r
|
||||
return nil
|
||||
}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resp.Config.ID, nil
|
||||
}
|
||||
|
||||
// RemoveConfig removes a config from a managed swarm cluster.
|
||||
func (c *Cluster) RemoveConfig(input string) error {
|
||||
return c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
|
||||
config, err := getConfig(ctx, state.controlClient, input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := &swarmapi.RemoveConfigRequest{
|
||||
ConfigID: config.ID,
|
||||
}
|
||||
|
||||
_, err = state.controlClient.RemoveConfig(ctx, req)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateConfig updates a config in a managed swarm cluster.
|
||||
// Note: this is not exposed to the CLI but is available from the API only
|
||||
func (c *Cluster) UpdateConfig(input string, version uint64, spec types.ConfigSpec) error {
|
||||
return c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
|
||||
config, err := getConfig(ctx, state.controlClient, input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configSpec := convert.ConfigSpecToGRPC(spec)
|
||||
|
||||
_, err = state.controlClient.UpdateConfig(ctx,
|
||||
&swarmapi.UpdateConfigRequest{
|
||||
ConfigID: config.ID,
|
||||
ConfigVersion: &swarmapi.Version{
|
||||
Index: version,
|
||||
},
|
||||
Spec: &configSpec,
|
||||
})
|
||||
return err
|
||||
})
|
||||
}
|
||||
61
daemon/cluster/convert/config.go
Normal file
61
daemon/cluster/convert/config.go
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
package convert
|
||||
|
||||
import (
|
||||
swarmtypes "github.com/docker/docker/api/types/swarm"
|
||||
swarmapi "github.com/docker/swarmkit/api"
|
||||
gogotypes "github.com/gogo/protobuf/types"
|
||||
)
|
||||
|
||||
// ConfigFromGRPC converts a grpc Config to a Config.
|
||||
func ConfigFromGRPC(s *swarmapi.Config) swarmtypes.Config {
|
||||
config := swarmtypes.Config{
|
||||
ID: s.ID,
|
||||
Spec: swarmtypes.ConfigSpec{
|
||||
Annotations: annotationsFromGRPC(s.Spec.Annotations),
|
||||
Data: s.Spec.Data,
|
||||
},
|
||||
}
|
||||
|
||||
config.Version.Index = s.Meta.Version.Index
|
||||
// Meta
|
||||
config.CreatedAt, _ = gogotypes.TimestampFromProto(s.Meta.CreatedAt)
|
||||
config.UpdatedAt, _ = gogotypes.TimestampFromProto(s.Meta.UpdatedAt)
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// ConfigSpecToGRPC converts Config to a grpc Config.
|
||||
func ConfigSpecToGRPC(s swarmtypes.ConfigSpec) swarmapi.ConfigSpec {
|
||||
return swarmapi.ConfigSpec{
|
||||
Annotations: swarmapi.Annotations{
|
||||
Name: s.Name,
|
||||
Labels: s.Labels,
|
||||
},
|
||||
Data: s.Data,
|
||||
}
|
||||
}
|
||||
|
||||
// ConfigReferencesFromGRPC converts a slice of grpc ConfigReference to ConfigReference
|
||||
func ConfigReferencesFromGRPC(s []*swarmapi.ConfigReference) []*swarmtypes.ConfigReference {
|
||||
refs := []*swarmtypes.ConfigReference{}
|
||||
|
||||
for _, r := range s {
|
||||
ref := &swarmtypes.ConfigReference{
|
||||
ConfigID: r.ConfigID,
|
||||
ConfigName: r.ConfigName,
|
||||
}
|
||||
|
||||
if t, ok := r.Target.(*swarmapi.ConfigReference_File); ok {
|
||||
ref.File = &swarmtypes.ConfigReferenceFileTarget{
|
||||
Name: t.File.Name,
|
||||
UID: t.File.UID,
|
||||
GID: t.File.GID,
|
||||
Mode: t.File.Mode,
|
||||
}
|
||||
}
|
||||
|
||||
refs = append(refs, ref)
|
||||
}
|
||||
|
||||
return refs
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) types.ContainerSpec {
|
|||
ReadOnly: c.ReadOnly,
|
||||
Hosts: c.Hosts,
|
||||
Secrets: secretReferencesFromGRPC(c.Secrets),
|
||||
Configs: configReferencesFromGRPC(c.Configs),
|
||||
}
|
||||
|
||||
if c.DNSConfig != nil {
|
||||
|
|
@ -137,6 +138,7 @@ func secretReferencesToGRPC(sr []*types.SecretReference) []*swarmapi.SecretRefer
|
|||
|
||||
return refs
|
||||
}
|
||||
|
||||
func secretReferencesFromGRPC(sr []*swarmapi.SecretReference) []*types.SecretReference {
|
||||
refs := make([]*types.SecretReference, 0, len(sr))
|
||||
for _, s := range sr {
|
||||
|
|
@ -161,6 +163,54 @@ func secretReferencesFromGRPC(sr []*swarmapi.SecretReference) []*types.SecretRef
|
|||
return refs
|
||||
}
|
||||
|
||||
func configReferencesToGRPC(sr []*types.ConfigReference) []*swarmapi.ConfigReference {
|
||||
refs := make([]*swarmapi.ConfigReference, 0, len(sr))
|
||||
for _, s := range sr {
|
||||
ref := &swarmapi.ConfigReference{
|
||||
ConfigID: s.ConfigID,
|
||||
ConfigName: s.ConfigName,
|
||||
}
|
||||
if s.File != nil {
|
||||
ref.Target = &swarmapi.ConfigReference_File{
|
||||
File: &swarmapi.FileTarget{
|
||||
Name: s.File.Name,
|
||||
UID: s.File.UID,
|
||||
GID: s.File.GID,
|
||||
Mode: s.File.Mode,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
refs = append(refs, ref)
|
||||
}
|
||||
|
||||
return refs
|
||||
}
|
||||
|
||||
func configReferencesFromGRPC(sr []*swarmapi.ConfigReference) []*types.ConfigReference {
|
||||
refs := make([]*types.ConfigReference, 0, len(sr))
|
||||
for _, s := range sr {
|
||||
target := s.GetFile()
|
||||
if target == nil {
|
||||
// not a file target
|
||||
logrus.Warnf("config target not a file: config=%s", s.ConfigID)
|
||||
continue
|
||||
}
|
||||
refs = append(refs, &types.ConfigReference{
|
||||
File: &types.ConfigReferenceFileTarget{
|
||||
Name: target.Name,
|
||||
UID: target.UID,
|
||||
GID: target.GID,
|
||||
Mode: target.Mode,
|
||||
},
|
||||
ConfigID: s.ConfigID,
|
||||
ConfigName: s.ConfigName,
|
||||
})
|
||||
}
|
||||
|
||||
return refs
|
||||
}
|
||||
|
||||
func containerToGRPC(c types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
|
||||
containerSpec := &swarmapi.ContainerSpec{
|
||||
Image: c.Image,
|
||||
|
|
@ -178,6 +228,7 @@ func containerToGRPC(c types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
|
|||
ReadOnly: c.ReadOnly,
|
||||
Hosts: c.Hosts,
|
||||
Secrets: secretReferencesToGRPC(c.Secrets),
|
||||
Configs: configReferencesToGRPC(c.Configs),
|
||||
}
|
||||
|
||||
if c.DNSConfig != nil {
|
||||
|
|
|
|||
|
|
@ -103,3 +103,19 @@ func newListSecretsFilters(filter filters.Args) (*swarmapi.ListSecretsRequest_Fi
|
|||
Labels: runconfigopts.ConvertKVStringsToMap(filter.Get("label")),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func newListConfigsFilters(filter filters.Args) (*swarmapi.ListConfigsRequest_Filters, error) {
|
||||
accepted := map[string]bool{
|
||||
"name": true,
|
||||
"id": true,
|
||||
"label": true,
|
||||
}
|
||||
if err := filter.Validate(accepted); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &swarmapi.ListConfigsRequest_Filters{
|
||||
NamePrefixes: filter.Get("name"),
|
||||
IDPrefixes: filter.Get("id"),
|
||||
Labels: runconfigopts.ConvertKVStringsToMap(filter.Get("label")),
|
||||
}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,3 +55,48 @@ func TestNewListSecretsFilters(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewListConfigsFilters(t *testing.T) {
|
||||
validNameFilter := filters.NewArgs()
|
||||
validNameFilter.Add("name", "test_name")
|
||||
|
||||
validIDFilter := filters.NewArgs()
|
||||
validIDFilter.Add("id", "7c9009d6720f6de3b492f5")
|
||||
|
||||
validLabelFilter := filters.NewArgs()
|
||||
validLabelFilter.Add("label", "type=test")
|
||||
validLabelFilter.Add("label", "storage=ssd")
|
||||
validLabelFilter.Add("label", "memory")
|
||||
|
||||
validAllFilter := filters.NewArgs()
|
||||
validAllFilter.Add("name", "nodeName")
|
||||
validAllFilter.Add("id", "7c9009d6720f6de3b492f5")
|
||||
validAllFilter.Add("label", "type=test")
|
||||
validAllFilter.Add("label", "memory")
|
||||
|
||||
validFilters := []filters.Args{
|
||||
validNameFilter,
|
||||
validIDFilter,
|
||||
validLabelFilter,
|
||||
validAllFilter,
|
||||
}
|
||||
|
||||
invalidTypeFilter := filters.NewArgs()
|
||||
invalidTypeFilter.Add("nonexist", "aaaa")
|
||||
|
||||
invalidFilters := []filters.Args{
|
||||
invalidTypeFilter,
|
||||
}
|
||||
|
||||
for _, filter := range validFilters {
|
||||
if _, err := newListConfigsFilters(filter); err != nil {
|
||||
t.Fatalf("Should get no error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, filter := range invalidFilters {
|
||||
if _, err := newListConfigsFilters(filter); err == nil {
|
||||
t.Fatalf("Should get an error for filter %s, while got nil", filter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -174,6 +174,42 @@ func getSecret(ctx context.Context, c swarmapi.ControlClient, input string) (*sw
|
|||
return rl.Secrets[0], nil
|
||||
}
|
||||
|
||||
func getConfig(ctx context.Context, c swarmapi.ControlClient, input string) (*swarmapi.Config, error) {
|
||||
// attempt to lookup config by full ID
|
||||
if rg, err := c.GetConfig(ctx, &swarmapi.GetConfigRequest{ConfigID: input}); err == nil {
|
||||
return rg.Config, nil
|
||||
}
|
||||
|
||||
// If any error (including NotFound), ListConfigs to match via full name.
|
||||
rl, err := c.ListConfigs(ctx, &swarmapi.ListConfigsRequest{
|
||||
Filters: &swarmapi.ListConfigsRequest_Filters{
|
||||
Names: []string{input},
|
||||
},
|
||||
})
|
||||
if err != nil || len(rl.Configs) == 0 {
|
||||
// If any error or 0 result, ListConfigs to match via ID prefix.
|
||||
rl, err = c.ListConfigs(ctx, &swarmapi.ListConfigsRequest{
|
||||
Filters: &swarmapi.ListConfigsRequest_Filters{
|
||||
IDPrefixes: []string{input},
|
||||
},
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(rl.Configs) == 0 {
|
||||
err := fmt.Errorf("config %s not found", input)
|
||||
return nil, errors.NewRequestNotFoundError(err)
|
||||
}
|
||||
|
||||
if l := len(rl.Configs); l > 1 {
|
||||
return nil, fmt.Errorf("config %s is ambiguous (%d matches found)", input, l)
|
||||
}
|
||||
|
||||
return rl.Configs[0], nil
|
||||
}
|
||||
|
||||
func getNetwork(ctx context.Context, c swarmapi.ControlClient, input string) (*swarmapi.Network, error) {
|
||||
// GetNetwork to match via full ID.
|
||||
if rg, err := c.GetNetwork(ctx, &swarmapi.GetNetworkRequest{NetworkID: input}); err == nil {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue