mirror of https://github.com/docker/cli.git
Updated GenericResource CLI
Signed-off-by: Renaud Gaubert <renaud.gaubert@gmail.com>
This commit is contained in:
parent
7843aec98c
commit
7ddd5f3434
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
"github.com/docker/cli/cli"
|
"github.com/docker/cli/cli"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
|
cliopts "github.com/docker/cli/opts"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/versions"
|
"github.com/docker/docker/api/types/versions"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -58,6 +59,9 @@ func newCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
flags.Var(&opts.hosts, flagHost, "Set one or more custom host-to-IP mappings (host:ip)")
|
flags.Var(&opts.hosts, flagHost, "Set one or more custom host-to-IP mappings (host:ip)")
|
||||||
flags.SetAnnotation(flagHost, "version", []string{"1.25"})
|
flags.SetAnnotation(flagHost, "version", []string{"1.25"})
|
||||||
|
|
||||||
|
flags.Var(cliopts.NewListOptsRef(&opts.resources.resGenericResources, ValidateSingleGenericResource), "generic-resource", "User defined resources")
|
||||||
|
flags.SetAnnotation(flagHostAdd, "version", []string{"1.32"})
|
||||||
|
|
||||||
flags.SetInterspersed(false)
|
flags.SetInterspersed(false)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/swarm"
|
||||||
|
swarmapi "github.com/docker/swarmkit/api"
|
||||||
|
"github.com/docker/swarmkit/api/genericresource"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenericResource is a concept that a user can use to advertise user-defined
|
||||||
|
// resources on a node and thus better place services based on these resources.
|
||||||
|
// E.g: NVIDIA GPUs, Intel FPGAs, ...
|
||||||
|
// See https://github.com/docker/swarmkit/blob/master/design/generic_resources.md
|
||||||
|
|
||||||
|
// ValidateSingleGenericResource validates that a single entry in the
|
||||||
|
// generic resource list is valid.
|
||||||
|
// i.e 'GPU=UID1' is valid however 'GPU:UID1' or 'UID1' isn't
|
||||||
|
func ValidateSingleGenericResource(val string) (string, error) {
|
||||||
|
if strings.Count(val, "=") < 1 {
|
||||||
|
return "", fmt.Errorf("invalid generic-resource format `%s` expected `name=value`", val)
|
||||||
|
}
|
||||||
|
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseGenericResources parses an array of Generic resourceResources
|
||||||
|
// Requesting Named Generic Resources for a service is not supported this
|
||||||
|
// is filtered here.
|
||||||
|
func ParseGenericResources(value []string) ([]swarm.GenericResource, error) {
|
||||||
|
if len(value) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
resources, err := genericresource.Parse(value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "invalid generic resource specification")
|
||||||
|
}
|
||||||
|
|
||||||
|
swarmResources := genericResourcesFromGRPC(resources)
|
||||||
|
for _, res := range swarmResources {
|
||||||
|
if res.NamedResourceSpec != nil {
|
||||||
|
return nil, fmt.Errorf("invalid generic-resource request `%s=%s`, Named Generic Resources is not supported for service create or update", res.NamedResourceSpec.Kind, res.NamedResourceSpec.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return swarmResources, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// genericResourcesFromGRPC converts a GRPC GenericResource to a GenericResource
|
||||||
|
func genericResourcesFromGRPC(genericRes []*swarmapi.GenericResource) []swarm.GenericResource {
|
||||||
|
var generic []swarm.GenericResource
|
||||||
|
for _, res := range genericRes {
|
||||||
|
var current swarm.GenericResource
|
||||||
|
|
||||||
|
switch r := res.Resource.(type) {
|
||||||
|
case *swarmapi.GenericResource_DiscreteResourceSpec:
|
||||||
|
current.DiscreteResourceSpec = &swarm.DiscreteGenericResource{
|
||||||
|
Kind: r.DiscreteResourceSpec.Kind,
|
||||||
|
Value: r.DiscreteResourceSpec.Value,
|
||||||
|
}
|
||||||
|
case *swarmapi.GenericResource_NamedResourceSpec:
|
||||||
|
current.NamedResourceSpec = &swarm.NamedGenericResource{
|
||||||
|
Kind: r.NamedResourceSpec.Kind,
|
||||||
|
Value: r.NamedResourceSpec.Value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generic = append(generic, current)
|
||||||
|
}
|
||||||
|
|
||||||
|
return generic
|
||||||
|
}
|
|
@ -226,9 +226,15 @@ type resourceOptions struct {
|
||||||
limitMemBytes opts.MemBytes
|
limitMemBytes opts.MemBytes
|
||||||
resCPU opts.NanoCPUs
|
resCPU opts.NanoCPUs
|
||||||
resMemBytes opts.MemBytes
|
resMemBytes opts.MemBytes
|
||||||
|
resGenericResources []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *resourceOptions) ToResourceRequirements() *swarm.ResourceRequirements {
|
func (r *resourceOptions) ToResourceRequirements() (*swarm.ResourceRequirements, error) {
|
||||||
|
generic, err := ParseGenericResources(r.resGenericResources)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &swarm.ResourceRequirements{
|
return &swarm.ResourceRequirements{
|
||||||
Limits: &swarm.Resources{
|
Limits: &swarm.Resources{
|
||||||
NanoCPUs: r.limitCPU.Value(),
|
NanoCPUs: r.limitCPU.Value(),
|
||||||
|
@ -237,8 +243,9 @@ func (r *resourceOptions) ToResourceRequirements() *swarm.ResourceRequirements {
|
||||||
Reservations: &swarm.Resources{
|
Reservations: &swarm.Resources{
|
||||||
NanoCPUs: r.resCPU.Value(),
|
NanoCPUs: r.resCPU.Value(),
|
||||||
MemoryBytes: r.resMemBytes.Value(),
|
MemoryBytes: r.resMemBytes.Value(),
|
||||||
|
GenericResources: generic,
|
||||||
},
|
},
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type restartPolicyOptions struct {
|
type restartPolicyOptions struct {
|
||||||
|
@ -588,6 +595,11 @@ func (options *serviceOptions) ToService(ctx context.Context, apiClient client.N
|
||||||
return service, err
|
return service, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resources, err := options.resources.ToResourceRequirements()
|
||||||
|
if err != nil {
|
||||||
|
return service, err
|
||||||
|
}
|
||||||
|
|
||||||
service = swarm.ServiceSpec{
|
service = swarm.ServiceSpec{
|
||||||
Annotations: swarm.Annotations{
|
Annotations: swarm.Annotations{
|
||||||
Name: options.name,
|
Name: options.name,
|
||||||
|
@ -619,7 +631,7 @@ func (options *serviceOptions) ToService(ctx context.Context, apiClient client.N
|
||||||
Isolation: container.Isolation(options.isolation),
|
Isolation: container.Isolation(options.isolation),
|
||||||
},
|
},
|
||||||
Networks: networks,
|
Networks: networks,
|
||||||
Resources: options.resources.ToResourceRequirements(),
|
Resources: resources,
|
||||||
RestartPolicy: options.restartPolicy.ToRestartPolicy(flags),
|
RestartPolicy: options.restartPolicy.ToRestartPolicy(flags),
|
||||||
Placement: &swarm.Placement{
|
Placement: &swarm.Placement{
|
||||||
Constraints: options.constraints.GetAll(),
|
Constraints: options.constraints.GetAll(),
|
||||||
|
|
Loading…
Reference in New Issue