2017-11-08 11:33:36 -05:00
package service
import (
"fmt"
"strings"
"github.com/pkg/errors"
"github.com/docker/docker/api/types/swarm"
2022-04-29 14:10:55 -04:00
swarmapi "github.com/moby/swarmkit/v2/api"
"github.com/moby/swarmkit/v2/api/genericresource"
2017-11-08 11:33:36 -05:00
)
// 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, ...
2022-04-29 14:10:55 -04:00
// See https://github.com/moby/swarmkit/blob/de950a7ed842c7b7e47e9451cde9bf8f96031894/design/generic_resources.md
2017-11-08 11:33:36 -05:00
// 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 {
2022-09-03 14:07:29 -04:00
generic := make ( [ ] swarm . GenericResource , 0 , len ( genericRes ) )
2017-11-08 11:33:36 -05:00
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
}
2017-11-17 17:05:44 -05:00
func buildGenericResourceMap ( genericRes [ ] swarm . GenericResource ) ( map [ string ] swarm . GenericResource , error ) {
m := make ( map [ string ] swarm . GenericResource )
for _ , res := range genericRes {
if res . DiscreteResourceSpec == nil {
return nil , fmt . Errorf ( "invalid generic-resource `%+v` for service task" , res )
}
_ , ok := m [ res . DiscreteResourceSpec . Kind ]
if ok {
return nil , fmt . Errorf ( "duplicate generic-resource `%+v` for service task" , res . DiscreteResourceSpec . Kind )
}
m [ res . DiscreteResourceSpec . Kind ] = res
}
return m , nil
}
func buildGenericResourceList ( genericRes map [ string ] swarm . GenericResource ) [ ] swarm . GenericResource {
2022-09-03 14:07:29 -04:00
l := make ( [ ] swarm . GenericResource , 0 , len ( genericRes ) )
2017-11-17 17:05:44 -05:00
for _ , res := range genericRes {
l = append ( l , res )
}
return l
}