DockerCLI/command/service/parse.go

69 lines
1.8 KiB
Go
Raw Normal View History

package service
import (
"fmt"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
swarmtypes "github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/client"
"golang.org/x/net/context"
)
// parseSecrets retrieves the secrets from the requested names and converts
// them to secret references to use with the spec
Update order of '--secret-rm' and '--secret-add' When using both `--secret-rm` and `--secret-add` on `docker service update`, `--secret-rm` was always performed last. This made it impossible to update a secret that was already in use on a service (for example, to change it's permissions, or mount-location inside the container). This patch changes the order in which `rm` and `add` are performed, allowing updating a secret in a single `docker service update`. Before this change, the `rm` was always performed "last", so the secret was always removed: $ echo "foo" | docker secret create foo -f - foo $ docker service create --name myservice --secret foo nginx:alpine 62xjcr9sr0c2hvepdzqrn3ssn $ docker service update --secret-rm foo --secret-add source=foo,target=foo2 myservice myservice $ docker service inspect --format '{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}' myservice | jq . null After this change, the `rm` is performed _first_, allowing users to update a secret without updating the service _twice_; $ echo "foo" | docker secret create foo -f - 1bllmvw3a1yaq3eixqw3f7bjl $ docker service create --name myservice --secret foo nginx:alpine lr6s3uoggli1x0hab78glpcxo $ docker service update --secret-rm foo --secret-add source=foo,target=foo2 myservice myservice $ docker service inspect --format '{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}' myservice | jq . [ { "File": { "Name": "foo2", "UID": "0", "GID": "0", "Mode": 292 }, "SecretID": "tn9qiblgnuuut11eufquw5dev", "SecretName": "foo" } ] Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2016-12-30 12:15:53 -05:00
func parseSecrets(client client.SecretAPIClient, requestedSecrets []*types.SecretRequestOption) ([]*swarmtypes.SecretReference, error) {
secretRefs := make(map[string]*swarmtypes.SecretReference)
ctx := context.Background()
for _, secret := range requestedSecrets {
if _, exists := secretRefs[secret.Target]; exists {
return nil, fmt.Errorf("duplicate secret target for %s not allowed", secret.Source)
}
secretRef := &swarmtypes.SecretReference{
File: &swarmtypes.SecretReferenceFileTarget{
Name: secret.Target,
UID: secret.UID,
GID: secret.GID,
Mode: secret.Mode,
},
SecretName: secret.Source,
}
secretRefs[secret.Target] = secretRef
}
args := filters.NewArgs()
for _, s := range secretRefs {
args.Add("names", s.SecretName)
}
secrets, err := client.SecretList(ctx, types.SecretListOptions{
Filters: args,
})
if err != nil {
return nil, err
}
foundSecrets := make(map[string]string)
for _, secret := range secrets {
foundSecrets[secret.Spec.Annotations.Name] = secret.ID
}
addedSecrets := []*swarmtypes.SecretReference{}
for _, ref := range secretRefs {
id, ok := foundSecrets[ref.SecretName]
if !ok {
return nil, fmt.Errorf("secret not found: %s", ref.SecretName)
}
// set the id for the ref to properly assign in swarm
// since swarm needs the ID instead of the name
ref.SecretID = id
addedSecrets = append(addedSecrets, ref)
}
return addedSecrets, nil
}