This code was using swarmkit's genericresource package as intermediate; add a local copy of that code that skips the protobufs as intermediate. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
117 lines
2.8 KiB
Go
117 lines
2.8 KiB
Go
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
|
//go:build go1.24
|
|
|
|
// Package genericresource is a local fork of SwarmKit's [genericresource] package,
|
|
// without protobuf dependencies.
|
|
//
|
|
// [genericresource]: https://github.com/moby/swarmkit/blob/v2.1.1/api/genericresource/parse.go
|
|
package genericresource
|
|
|
|
import (
|
|
"encoding/csv"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
api "github.com/moby/moby/api/types/swarm"
|
|
)
|
|
|
|
func newParseError(format string, args ...any) error {
|
|
return fmt.Errorf("could not parse GenericResource: "+format, args...)
|
|
}
|
|
|
|
// discreteResourceVal returns an int64 if the string is a discreteResource
|
|
// and an error if it isn't
|
|
func discreteResourceVal(res string) (int64, error) {
|
|
return strconv.ParseInt(res, 10, 64)
|
|
}
|
|
|
|
// allNamedResources returns true if the array of resources are all namedResources
|
|
// e.g: res = [red, orange, green]
|
|
func allNamedResources(res []string) bool {
|
|
for _, v := range res {
|
|
if _, err := discreteResourceVal(v); err == nil {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// ParseCmd parses the Generic Resource command line argument
|
|
// and returns a list of api.GenericResource
|
|
func ParseCmd(cmd string) ([]api.GenericResource, error) {
|
|
if strings.Contains(cmd, "\n") {
|
|
return nil, newParseError("unexpected '\\n' character")
|
|
}
|
|
|
|
r := csv.NewReader(strings.NewReader(cmd))
|
|
records, err := r.ReadAll()
|
|
if err != nil {
|
|
return nil, newParseError("%v", err)
|
|
}
|
|
|
|
if len(records) != 1 {
|
|
return nil, newParseError("found multiple records while parsing cmd %v", records)
|
|
}
|
|
|
|
return Parse(records[0])
|
|
}
|
|
|
|
// Parse parses a table of GenericResource resources
|
|
func Parse(cmds []string) ([]api.GenericResource, error) {
|
|
tokens := make(map[string][]string)
|
|
|
|
for _, term := range cmds {
|
|
kva := strings.Split(term, "=")
|
|
if len(kva) != 2 {
|
|
return nil, newParseError("incorrect term %s, missing"+
|
|
" '=' or malformed expression", term)
|
|
}
|
|
|
|
key := strings.TrimSpace(kva[0])
|
|
val := strings.TrimSpace(kva[1])
|
|
|
|
tokens[key] = append(tokens[key], val)
|
|
}
|
|
|
|
var rs []api.GenericResource
|
|
for k, v := range tokens {
|
|
if u, ok := isDiscreteResource(v); ok {
|
|
if u < 0 {
|
|
return nil, newParseError("cannot ask for"+
|
|
" negative resource %s", k)
|
|
}
|
|
|
|
rs = append(rs, NewDiscrete(k, u))
|
|
continue
|
|
}
|
|
|
|
if allNamedResources(v) {
|
|
rs = append(rs, NewSet(k, v...)...)
|
|
continue
|
|
}
|
|
|
|
return nil, newParseError("mixed discrete and named resources"+
|
|
" in expression '%s=%s'", k, v)
|
|
}
|
|
|
|
return rs, nil
|
|
}
|
|
|
|
// isDiscreteResource returns true if the array of resources is a
|
|
// Discrete Resource.
|
|
// e.g: res = [1]
|
|
func isDiscreteResource(values []string) (int64, bool) {
|
|
if len(values) != 1 {
|
|
return int64(0), false
|
|
}
|
|
|
|
u, err := discreteResourceVal(values[0])
|
|
if err != nil {
|
|
return int64(0), false
|
|
}
|
|
|
|
return u, true
|
|
}
|