Replace strings.SplitN with strings.Cut

Cut is a cleaner & more performant api relative to SplitN(_, _, 2) added in go 1.18

Previously applied this refactoring to buildah:
https://github.com/containers/buildah/pull/5239

Signed-off-by: Philip Dubé <philip@peerdb.io>
This commit is contained in:
Philip Dubé 2024-01-02 18:31:25 +00:00
parent f1ea4fbb3d
commit 522934d5cf
56 changed files with 596 additions and 678 deletions

View file

@ -333,15 +333,15 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *Buil
}
if c.Flag("build-arg").Changed {
for _, arg := range flags.BuildArg {
av := strings.SplitN(arg, "=", 2)
if len(av) > 1 {
args[av[0]] = av[1]
key, val, hasVal := strings.Cut(arg, "=")
if hasVal {
args[key] = val
} else {
// check if the env is set in the local environment and use that value if it is
if val, present := os.LookupEnv(av[0]); present {
args[av[0]] = val
if val, present := os.LookupEnv(key); present {
args[key] = val
} else {
delete(args, av[0])
delete(args, key)
}
}
}
@ -450,15 +450,15 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *Buil
additionalBuildContext := make(map[string]*buildahDefine.AdditionalBuildContext)
if c.Flag("build-context").Changed {
for _, contextString := range flags.BuildContext {
av := strings.SplitN(contextString, "=", 2)
if len(av) > 1 {
parseAdditionalBuildContext, err := parse.GetAdditionalBuildContext(av[1])
key, val, hasVal := strings.Cut(contextString, "=")
if hasVal {
parseAdditionalBuildContext, err := parse.GetAdditionalBuildContext(val)
if err != nil {
return nil, fmt.Errorf("while parsing additional build context: %w", err)
}
additionalBuildContext[av[0]] = &parseAdditionalBuildContext
additionalBuildContext[key] = &parseAdditionalBuildContext
} else {
return nil, fmt.Errorf("while parsing additional build context: %q, accepts value in the form of key=value", av)
return nil, fmt.Errorf("while parsing additional build context: %s, accepts value in the form of key=value", contextString)
}
}
}

View file

@ -423,9 +423,9 @@ func prefixSlice(pre string, slice []string) []string {
func suffixCompSlice(suf string, slice []string) []string {
for i := range slice {
split := strings.SplitN(slice[i], "\t", 2)
if len(split) > 1 {
slice[i] = split[0] + suf + "\t" + split[1]
key, val, hasVal := strings.Cut(slice[i], "\t")
if hasVal {
slice[i] = key + suf + "\t" + val
} else {
slice[i] += suf
}
@ -878,10 +878,10 @@ func AutocompleteScp(cmd *cobra.Command, args []string, toComplete string) ([]st
}
switch len(args) {
case 0:
split := strings.SplitN(toComplete, "::", 2)
if len(split) > 1 {
imageSuggestions, _ := getImages(cmd, split[1])
return prefixSlice(split[0]+"::", imageSuggestions), cobra.ShellCompDirectiveNoFileComp
prefix, imagesToComplete, isImages := strings.Cut(toComplete, "::")
if isImages {
imageSuggestions, _ := getImages(cmd, imagesToComplete)
return prefixSlice(prefix+"::", imageSuggestions), cobra.ShellCompDirectiveNoFileComp
}
connectionSuggestions, _ := AutocompleteSystemConnections(cmd, args, toComplete)
imageSuggestions, _ := getImages(cmd, toComplete)
@ -893,9 +893,9 @@ func AutocompleteScp(cmd *cobra.Command, args []string, toComplete string) ([]st
}
return totalSuggestions, directive
case 1:
split := strings.SplitN(args[0], "::", 2)
if len(split) > 1 {
if len(split[1]) > 0 {
_, imagesToComplete, isImages := strings.Cut(args[0], "::")
if isImages {
if len(imagesToComplete) > 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
imageSuggestions, _ := getImages(cmd, toComplete)
@ -1076,7 +1076,7 @@ func AutocompleteUserFlag(cmd *cobra.Command, args []string, toComplete string)
var groups []string
scanner := bufio.NewScanner(file)
user := strings.SplitN(toComplete, ":", 2)[0]
user, _, _ := strings.Cut(toComplete, ":")
for scanner.Scan() {
entries := strings.SplitN(scanner.Text(), ":", 4)
groups = append(groups, user+":"+entries[0])

View file

@ -342,10 +342,10 @@ func PullImage(imageName string, cliVals *entities.ContainerCreateOptions) (stri
if cliVals.Arch != "" || cliVals.OS != "" {
return "", errors.New("--platform option can not be specified with --arch or --os")
}
split := strings.SplitN(cliVals.Platform, "/", 2)
cliVals.OS = split[0]
if len(split) > 1 {
cliVals.Arch = split[1]
OS, Arch, hasArch := strings.Cut(cliVals.Platform, "/")
cliVals.OS = OS
if hasArch {
cliVals.Arch = Arch
}
}
}

View file

@ -102,11 +102,11 @@ func pause(cmd *cobra.Command, args []string) error {
}
for _, f := range filters {
split := strings.SplitN(f, "=", 2)
if len(split) < 2 {
fname, filter, hasFilter := strings.Cut(f, "=")
if !hasFilter {
return fmt.Errorf("invalid filter %q", f)
}
pauseOpts.Filters[split[0]] = append(pauseOpts.Filters[split[0]], split[1])
pauseOpts.Filters[fname] = append(pauseOpts.Filters[fname], filter)
}
responses, err := registry.ContainerEngine().ContainerPause(context.Background(), args, pauseOpts)

View file

@ -195,11 +195,11 @@ func ps(cmd *cobra.Command, _ []string) error {
}
for _, f := range filters {
split := strings.SplitN(f, "=", 2)
if len(split) == 1 {
fname, filter, hasFilter := strings.Cut(f, "=")
if !hasFilter {
return fmt.Errorf("invalid filter %q", f)
}
listOpts.Filters[split[0]] = append(listOpts.Filters[split[0]], split[1])
listOpts.Filters[fname] = append(listOpts.Filters[fname], filter)
}
listContainers, err := getResponses()
if err != nil {

View file

@ -114,11 +114,11 @@ func restart(cmd *cobra.Command, args []string) error {
}
for _, f := range filters {
split := strings.SplitN(f, "=", 2)
if len(split) < 2 {
fname, filter, hasFilter := strings.Cut(f, "=")
if !hasFilter {
return fmt.Errorf("invalid filter %q", f)
}
restartOpts.Filters[split[0]] = append(restartOpts.Filters[split[0]], split[1])
restartOpts.Filters[fname] = append(restartOpts.Filters[fname], filter)
}
responses, err := registry.ContainerEngine().ContainerRestart(context.Background(), args, restartOpts)

View file

@ -116,16 +116,16 @@ func rm(cmd *cobra.Command, args []string) error {
}
return fmt.Errorf("reading CIDFile: %w", err)
}
id := strings.Split(string(content), "\n")[0]
id, _, _ := strings.Cut(string(content), "\n")
args = append(args, id)
}
for _, f := range filters {
split := strings.SplitN(f, "=", 2)
if len(split) < 2 {
fname, filter, hasFilter := strings.Cut(f, "=")
if !hasFilter {
return fmt.Errorf("invalid filter %q", f)
}
rmOptions.Filters[split[0]] = append(rmOptions.Filters[split[0]], split[1])
rmOptions.Filters[fname] = append(rmOptions.Filters[fname], filter)
}
if rmOptions.All {

View file

@ -124,11 +124,11 @@ func start(cmd *cobra.Command, args []string) error {
containers := utils.RemoveSlash(args)
for _, f := range filters {
split := strings.SplitN(f, "=", 2)
if len(split) < 2 {
fname, filter, hasFilter := strings.Cut(f, "=")
if !hasFilter {
return fmt.Errorf("invalid filter %q", f)
}
startOptions.Filters[split[0]] = append(startOptions.Filters[split[0]], split[1])
startOptions.Filters[fname] = append(startOptions.Filters[fname], filter)
}
responses, err := registry.ContainerEngine().ContainerStart(registry.GetContext(), containers, startOptions)

View file

@ -119,11 +119,11 @@ func stop(cmd *cobra.Command, args []string) error {
}
for _, f := range filters {
split := strings.SplitN(f, "=", 2)
if len(split) < 2 {
fname, filter, hasFilter := strings.Cut(f, "=")
if !hasFilter {
return fmt.Errorf("invalid filter %q", f)
}
stopOptions.Filters[split[0]] = append(stopOptions.Filters[split[0]], split[1])
stopOptions.Filters[fname] = append(stopOptions.Filters[fname], filter)
}
responses, err := registry.ContainerEngine().ContainerStop(context.Background(), args, stopOptions)

View file

@ -110,11 +110,11 @@ func unpause(cmd *cobra.Command, args []string) error {
}
for _, f := range filters {
split := strings.SplitN(f, "=", 2)
if len(split) < 2 {
fname, filter, hasFilter := strings.Cut(f, "=")
if !hasFilter {
return fmt.Errorf("invalid filter %q", f)
}
unpauseOpts.Filters[split[0]] = append(unpauseOpts.Filters[split[0]], split[1])
unpauseOpts.Filters[fname] = append(unpauseOpts.Filters[fname], filter)
}
responses, err := registry.ContainerEngine().ContainerUnpause(context.Background(), args, unpauseOpts)

View file

@ -149,10 +149,14 @@ func imagePull(cmd *cobra.Command, args []string) error {
if pullOptions.Arch != "" || pullOptions.OS != "" {
return errors.New("--platform option can not be specified with --arch or --os")
}
split := strings.SplitN(platform, "/", 2)
pullOptions.OS = split[0]
if len(split) > 1 {
pullOptions.Arch = split[1]
specs := strings.Split(platform, "/")
pullOptions.OS = specs[0] // may be empty
if len(specs) > 1 {
pullOptions.Arch = specs[1]
if len(specs) > 2 {
pullOptions.Variant = specs[2]
}
}
}

View file

@ -246,18 +246,17 @@ func play(cmd *cobra.Command, args []string) error {
}
for _, annotation := range playOptions.annotations {
splitN := strings.SplitN(annotation, "=", 2)
if len(splitN) != 2 {
key, val, hasVal := strings.Cut(annotation, "=")
if !hasVal {
return fmt.Errorf("annotation %q must include an '=' sign", annotation)
}
if playOptions.Annotations == nil {
playOptions.Annotations = make(map[string]string)
}
annotation := splitN[1]
if len(annotation) > define.MaxKubeAnnotation && !playOptions.UseLongAnnotations {
return fmt.Errorf("annotation exceeds maximum size, %d, of kubernetes annotation: %s", define.MaxKubeAnnotation, annotation)
if len(val) > define.MaxKubeAnnotation && !playOptions.UseLongAnnotations {
return fmt.Errorf("annotation exceeds maximum size, %d, of kubernetes annotation: %s", define.MaxKubeAnnotation, val)
}
playOptions.Annotations[splitN[0]] = annotation
playOptions.Annotations[key] = val
}
for _, mac := range playOptions.macs {

View file

@ -245,16 +245,16 @@ func parseRoute(routeStr string) (*types.Route, error) {
}
func parseRange(iprange string) (*types.LeaseRange, error) {
split := strings.SplitN(iprange, "-", 2)
if len(split) > 1 {
startIPString, endIPString, hasDash := strings.Cut(iprange, "-")
if hasDash {
// range contains dash so assume form is start-end
start := net.ParseIP(split[0])
start := net.ParseIP(startIPString)
if start == nil {
return nil, fmt.Errorf("range start ip %q is not a ip address", split[0])
return nil, fmt.Errorf("range start ip %q is not a ip address", startIPString)
}
end := net.ParseIP(split[1])
end := net.ParseIP(endIPString)
if end == nil {
return nil, fmt.Errorf("range end ip %q is not a ip address", split[1])
return nil, fmt.Errorf("range end ip %q is not a ip address", endIPString)
}
return &types.LeaseRange{
StartIP: start,

View file

@ -9,11 +9,11 @@ import (
func FilterArgumentsIntoFilters(filters []string) (url.Values, error) {
parsedFilters := make(url.Values)
for _, f := range filters {
t := strings.SplitN(f, "=", 2)
if len(t) < 2 {
fname, filter, hasFilter := strings.Cut(f, "=")
if !hasFilter {
return parsedFilters, fmt.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
}
parsedFilters.Add(t[0], t[1])
parsedFilters.Add(fname, filter)
}
return parsedFilters, nil
}

View file

@ -33,15 +33,15 @@ var (
// for add-host flag
func ValidateExtraHost(val string) (string, error) {
// allow for IPv6 addresses in extra hosts by only splitting on first ":"
arr := strings.SplitN(val, ":", 2)
if len(arr) != 2 || len(arr[0]) == 0 {
name, ip, hasIP := strings.Cut(val, ":")
if !hasIP || len(name) == 0 {
return "", fmt.Errorf("bad format for add-host: %q", val)
}
if arr[1] == etchosts.HostGateway {
if ip == etchosts.HostGateway {
return val, nil
}
if _, err := validateIPAddress(arr[1]); err != nil {
return "", fmt.Errorf("invalid IP address in add-host: %q", arr[1])
if _, err := validateIPAddress(ip); err != nil {
return "", fmt.Errorf("invalid IP address in add-host: %q", ip)
}
return val, nil
}
@ -82,45 +82,37 @@ func GetAllLabels(labelFile, inputLabels []string) (map[string]string, error) {
}
}
for _, label := range inputLabels {
split := strings.SplitN(label, "=", 2)
if split[0] == "" {
key, value, _ := strings.Cut(label, "=")
if key == "" {
return nil, fmt.Errorf("invalid label format: %q", label)
}
value := ""
if len(split) > 1 {
value = split[1]
}
labels[split[0]] = value
labels[key] = value
}
return labels, nil
}
func parseEnvOrLabel(env map[string]string, line, configType string) error {
data := strings.SplitN(line, "=", 2)
key, val, hasVal := strings.Cut(line, "=")
// catch invalid variables such as "=" or "=A"
if data[0] == "" {
if key == "" {
return fmt.Errorf("invalid environment variable: %q", line)
}
// trim the front of a variable, but nothing else
name := strings.TrimLeft(data[0], whiteSpaces)
name := strings.TrimLeft(key, whiteSpaces)
if strings.ContainsAny(name, whiteSpaces) {
return fmt.Errorf("name %q has white spaces, poorly formatted name", name)
}
if len(data) > 1 {
env[name] = data[1]
if hasVal {
env[name] = val
} else {
if strings.HasSuffix(name, "*") {
name = strings.TrimSuffix(name, "*")
if name, hasStar := strings.CutSuffix(name, "*"); hasStar {
for _, e := range os.Environ() {
part := strings.SplitN(e, "=", 2)
if len(part) < 2 {
continue
}
if strings.HasPrefix(part[0], name) {
env[part[0]] = part[1]
envKey, envVal, hasEq := strings.Cut(e, "=")
if hasEq && strings.HasPrefix(envKey, name) {
env[envKey] = envVal
}
}
} else if configType == ENVType {

View file

@ -81,11 +81,11 @@ func pods(cmd *cobra.Command, _ []string) error {
if cmd.Flag("filter").Changed {
psInput.Filters = make(map[string][]string)
for _, f := range inputFilters {
split := strings.SplitN(f, "=", 2)
if len(split) < 2 {
fname, filter, hasFilter := strings.Cut(f, "=")
if !hasFilter {
return fmt.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
}
psInput.Filters[split[0]] = append(psInput.Filters[split[0]], split[1])
psInput.Filters[fname] = append(psInput.Filters[fname], filter)
}
}
responses, err := registry.ContainerEngine().PodPs(context.Background(), psInput)

View file

@ -261,15 +261,15 @@ func translateDest(path string) (string, error) {
if path == "" {
return "", nil
}
split := strings.SplitN(path, "=", 2)
if len(split) == 1 {
return split[0], nil
key, val, hasVal := strings.Cut(path, "=")
if !hasVal {
return key, nil
}
if split[0] != "host" {
return "", fmt.Errorf("\"host\" is requited for --docker option")
if key != "host" {
return "", fmt.Errorf("\"host\" is required for --docker option")
}
// "host=tcp://myserver:2376,ca=~/ca-file,cert=~/cert-file,key=~/key-file"
vals := strings.Split(split[1], ",")
vals := strings.Split(val, ",")
if len(vals) > 1 {
return "", fmt.Errorf("--docker additional options %q not supported", strings.Join(vals[1:], ","))
}

View file

@ -97,8 +97,8 @@ func (c *Container) Commit(ctx context.Context, destImage string, options Contai
// Should we store the ENV we actually want in the spec separately?
if c.config.Spec.Process != nil {
for _, e := range c.config.Spec.Process.Env {
splitEnv := strings.SplitN(e, "=", 2)
importBuilder.SetEnv(splitEnv[0], splitEnv[1])
key, val, _ := strings.Cut(e, "=")
importBuilder.SetEnv(key, val)
}
}
// Expose ports

View file

@ -254,7 +254,7 @@ func (c *Container) addSharedNamespaces(g *generate.Generator) error {
}
needEnv := true
for _, checkEnv := range g.Config.Process.Env {
if strings.SplitN(checkEnv, "=", 2)[0] == "HOSTNAME" {
if strings.HasPrefix(checkEnv, "HOSTNAME=") {
needEnv = false
break
}

View file

@ -73,13 +73,13 @@ func (p *Pod) GenerateForKube(ctx context.Context, getService, useLongAnnotation
return nil, servicePorts, err
}
for _, host := range infraContainer.config.ContainerNetworkConfig.HostAdd {
hostSli := strings.SplitN(host, ":", 2)
if len(hostSli) != 2 {
hostname, ip, hasIP := strings.Cut(host, ":")
if !hasIP {
return nil, servicePorts, errors.New("invalid hostAdd")
}
extraHost = append(extraHost, v1.HostAlias{
IP: hostSli[1],
Hostnames: []string{hostSli[0]},
IP: ip,
Hostnames: []string{hostname},
})
}
ports, err = portMappingToContainerPort(infraContainer.config.PortMappings, getService)
@ -1001,10 +1001,10 @@ func containerToV1Container(ctx context.Context, c *Container, getService bool)
dnsOptions := make([]v1.PodDNSConfigOption, 0)
for _, option := range options {
// the option can be "k:v" or just "k", no delimiter is required
opts := strings.SplitN(option, ":", 2)
name, value, _ := strings.Cut(option, ":")
dnsOpt := v1.PodDNSConfigOption{
Name: opts[0],
Value: &opts[1],
Name: name,
Value: &value,
}
dnsOptions = append(dnsOptions, dnsOpt)
}
@ -1055,23 +1055,23 @@ func libpodEnvVarsToKubeEnvVars(envs []string, imageEnvs []string) ([]v1.EnvVar,
envVars := make([]v1.EnvVar, 0, len(envs))
imageMap := make(map[string]string, len(imageEnvs))
for _, ie := range imageEnvs {
split := strings.SplitN(ie, "=", 2)
imageMap[split[0]] = split[1]
key, val, _ := strings.Cut(ie, "=")
imageMap[key] = val
}
for _, e := range envs {
split := strings.SplitN(e, "=", 2)
if len(split) != 2 {
envName, envValue, hasValue := strings.Cut(e, "=")
if !hasValue {
return envVars, fmt.Errorf("environment variable %s is malformed; should be key=value", e)
}
if defaultEnv[split[0]] == split[1] {
if defaultEnv[envName] == envValue {
continue
}
if imageMap[split[0]] == split[1] {
if imageMap[envName] == envValue {
continue
}
ev := v1.EnvVar{
Name: split[0],
Value: split[1],
Name: envName,
Value: envValue,
}
envVars = append(envVars, ev)
}
@ -1286,25 +1286,22 @@ func generateKubeSecurityContext(c *Container) (*v1.SecurityContext, bool, error
var selinuxOpts v1.SELinuxOptions
selinuxHasData := false
for _, label := range strings.Split(c.config.Spec.Annotations[define.InspectAnnotationLabel], ",label=") {
opts := strings.SplitN(label, ":", 2)
switch len(opts) {
case 2:
switch opts[0] {
opt, val, hasVal := strings.Cut(label, ":")
if hasVal {
switch opt {
case "filetype":
selinuxOpts.FileType = opts[1]
selinuxOpts.FileType = val
selinuxHasData = true
case "type":
selinuxOpts.Type = opts[1]
selinuxOpts.Type = val
selinuxHasData = true
case "level":
selinuxOpts.Level = opts[1]
selinuxHasData = true
}
case 1:
if opts[0] == "disable" {
selinuxOpts.Type = "spc_t"
selinuxOpts.Level = val
selinuxHasData = true
}
} else if opt == "disable" {
selinuxOpts.Type = "spc_t"
selinuxHasData = true
}
}
if selinuxHasData {

View file

@ -524,11 +524,11 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON,
exposedPorts := make(nat.PortSet)
for ep := range inspect.NetworkSettings.Ports {
splitp := strings.SplitN(ep, "/", 2)
if len(splitp) != 2 {
port, proto, ok := strings.Cut(ep, "/")
if !ok {
return nil, fmt.Errorf("PORT/PROTOCOL Format required for %q", ep)
}
exposedPort, err := nat.NewPort(splitp[1], splitp[0])
exposedPort, err := nat.NewPort(proto, port)
if err != nil {
return nil, err
}

View file

@ -49,12 +49,12 @@ func ExecCreateHandler(w http.ResponseWriter, r *http.Request) {
}
libpodConfig.Environment = make(map[string]string)
for _, envStr := range input.Env {
split := strings.SplitN(envStr, "=", 2)
if len(split) != 2 {
key, val, hasVal := strings.Cut(envStr, "=")
if !hasVal {
utils.Error(w, http.StatusBadRequest, fmt.Errorf("environment variable %q badly formed, must be key=value", envStr))
return
}
libpodConfig.Environment[split[0]] = split[1]
libpodConfig.Environment[key] = val
}
libpodConfig.WorkDir = input.WorkingDir
libpodConfig.Privileged = input.Privileged

View file

@ -330,15 +330,15 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
if len(secretOpt) > 0 {
modifiedOpt := []string{}
for _, token := range secretOpt {
arr := strings.SplitN(token, "=", 2)
if len(arr) > 1 {
if arr[0] == "src" {
key, val, hasVal := strings.Cut(token, "=")
if hasVal {
if key == "src" {
/* move secret away from contextDir */
/* to make sure we dont accidentally commit temporary secrets to image*/
builderDirectory, _ := filepath.Split(contextDirectory)
// following path is outside build context
newSecretPath := filepath.Join(builderDirectory, arr[1])
oldSecretPath := filepath.Join(contextDirectory, arr[1])
newSecretPath := filepath.Join(builderDirectory, val)
oldSecretPath := filepath.Join(contextDirectory, val)
err := os.Rename(oldSecretPath, newSecretPath)
if err != nil {
utils.BadRequest(w, "secrets", query.Secrets, err)
@ -551,19 +551,19 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
utils.BadRequest(w, "securityopt", query.SecurityOpt, errors.New("no-new-privileges is not supported"))
return
}
con := strings.SplitN(opt, "=", 2)
if len(con) != 2 {
name, value, hasValue := strings.Cut(opt, "=")
if !hasValue {
utils.BadRequest(w, "securityopt", query.SecurityOpt, fmt.Errorf("invalid --security-opt name=value pair: %q", opt))
return
}
switch con[0] {
switch name {
case "label":
labelOpts = append(labelOpts, con[1])
labelOpts = append(labelOpts, value)
case "apparmor":
apparmor = con[1]
apparmor = value
case "seccomp":
seccomp = con[1]
seccomp = value
default:
utils.BadRequest(w, "securityopt", query.SecurityOpt, fmt.Errorf("invalid --security-opt 2: %q", opt))
return

View file

@ -489,12 +489,12 @@ func ManifestModify(w http.ResponseWriter, r *http.Request) {
}
annotations := make(map[string]string)
for _, annotationSpec := range body.ManifestAddOptions.Annotation {
spec := strings.SplitN(annotationSpec, "=", 2)
if len(spec) != 2 {
utils.Error(w, http.StatusBadRequest, fmt.Errorf("no value given for annotation %q", spec[0]))
key, val, hasVal := strings.Cut(annotationSpec, "=")
if !hasVal {
utils.Error(w, http.StatusBadRequest, fmt.Errorf("no value given for annotation %q", key))
return
}
annotations[spec[0]] = spec[1]
annotations[key] = val
}
body.ManifestAddOptions.Annotations = envLib.Join(body.ManifestAddOptions.Annotations, annotations)
body.ManifestAddOptions.Annotation = nil

View file

@ -268,7 +268,7 @@ func normalizeAuthFileKey(authFileKey string) string {
stripped = strings.TrimPrefix(stripped, "https://")
if stripped != authFileKey { // URLs are interpreted to mean complete registries
stripped = strings.SplitN(stripped, "/", 2)[0]
stripped, _, _ = strings.Cut(stripped, "/")
}
// Only non-namespaced registry names (or URLs) need to be normalized; repo namespaces

View file

@ -530,9 +530,9 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
if len(secretOpt) > 0 {
modifiedOpt := []string{}
for _, token := range secretOpt {
arr := strings.SplitN(token, "=", 2)
if len(arr) > 1 {
if arr[0] == "src" {
opt, val, hasVal := strings.Cut(token, "=")
if hasVal {
if opt == "src" {
// read specified secret into a tmp file
// move tmp file to tar and change secret source to relative tmp file
tmpSecretFile, err := os.CreateTemp(options.ContextDirectory, "podman-build-secret")
@ -541,7 +541,7 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
}
defer os.Remove(tmpSecretFile.Name()) // clean up
defer tmpSecretFile.Close()
srcSecretFile, err := os.Open(arr[1])
srcSecretFile, err := os.Open(val)
if err != nil {
return nil, err
}

View file

@ -40,9 +40,9 @@ func parseUserInput(input string) (container string, path string) {
return
}
if spl := strings.SplitN(path, ":", 2); len(spl) == 2 {
container = spl[0]
path = spl[1]
if parsedContainer, parsedPath, ok := strings.Cut(path, ":"); ok {
container = parsedContainer
path = parsedPath
}
return
}

View file

@ -98,10 +98,10 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo
var imageNameWithoutTag string
// Compare with ImageID, ImageName
// Will match ImageName if running image has tag latest for other tags exact complete filter must be given
imageNameSlice := strings.SplitN(rootfsImageName, ":", 2)
if len(imageNameSlice) == 2 {
imageNameWithoutTag = imageNameSlice[0]
imageTag = imageNameSlice[1]
name, tag, hasColon := strings.Cut(rootfsImageName, ":")
if hasColon {
imageNameWithoutTag = name
imageTag = tag
}
if (rootfsImageID == filterValue) ||
@ -144,13 +144,8 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo
//- volume=(<volume-name>|<mount-point-destination>)
return func(c *libpod.Container) bool {
containerConfig := c.ConfigNoCopy()
var dest string
for _, filterValue := range filterValues {
arr := strings.SplitN(filterValue, ":", 2)
source := arr[0]
if len(arr) == 2 {
dest = arr[1]
}
source, dest, _ := strings.Cut(filterValue, ":")
for _, mount := range containerConfig.Spec.Mounts {
if dest != "" && (mount.Source == source && mount.Destination == dest) {
return true
@ -233,19 +228,10 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo
// check if networkMode is configured as `container:<ctr>`
// perform a match against filter `container:<IDorName>`
// networks is already going to be empty if `container:<ctr>` is configured as Mode
if strings.HasPrefix(networkMode, "container:") {
networkModeContainerPart := strings.SplitN(networkMode, ":", 2)
if len(networkModeContainerPart) < 2 {
return false
}
networkModeContainerID := networkModeContainerPart[1]
if networkModeContainerID, ok := strings.CutPrefix(networkMode, "container:"); ok {
for _, val := range filterValues {
if strings.HasPrefix(val, "container:") {
filterNetworkModePart := strings.SplitN(val, ":", 2)
if len(filterNetworkModePart) < 2 {
return false
}
filterNetworkModeIDorName := filterNetworkModePart[1]
if idOrName, ok := strings.CutPrefix(val, "container:"); ok {
filterNetworkModeIDorName := idOrName
filterID, err := r.LookupContainerID(filterNetworkModeIDorName)
if err != nil {
return false

View file

@ -49,14 +49,7 @@ func GenerateVolumeFilters(filter string, filterValues []string, runtime *libpod
case "opt":
return func(v *libpod.Volume) bool {
for _, val := range filterValues {
filterArray := strings.SplitN(val, "=", 2)
filterKey := filterArray[0]
var filterVal string
if len(filterArray) > 1 {
filterVal = filterArray[1]
} else {
filterVal = ""
}
filterKey, filterVal, _ := strings.Cut(val, "=")
for labelKey, labelValue := range v.Options() {
if labelKey == filterKey && (filterVal == "" || labelValue == filterVal) {

View file

@ -135,7 +135,7 @@ func generateRunlabelCommand(runlabel string, img *libimage.Image, inputName str
name = splitImageName[len(splitImageName)-1]
// make sure to remove the tag from the image name, otherwise the name cannot
// be used as container name because a colon is an illegal character
name = strings.SplitN(name, ":", 2)[0]
name, _, _ = strings.Cut(name, ":")
}
// Append the user-specified arguments to the runlabel (command).

View file

@ -230,11 +230,11 @@ func (ir *ImageEngine) ManifestAdd(ctx context.Context, name string, images []st
if len(opts.Annotation) != 0 {
annotations := make(map[string]string)
for _, annotationSpec := range opts.Annotation {
spec := strings.SplitN(annotationSpec, "=", 2)
if len(spec) != 2 {
return "", fmt.Errorf("no value given for annotation %q", spec[0])
key, val, hasVal := strings.Cut(annotationSpec, "=")
if !hasVal {
return "", fmt.Errorf("no value given for annotation %q", key)
}
annotations[spec[0]] = spec[1]
annotations[key] = val
}
opts.Annotations = envLib.Join(opts.Annotations, annotations)
}
@ -269,11 +269,11 @@ func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, name, image string,
if len(opts.Annotation) != 0 {
annotations := make(map[string]string)
for _, annotationSpec := range opts.Annotation {
spec := strings.SplitN(annotationSpec, "=", 2)
if len(spec) != 2 {
return "", fmt.Errorf("no value given for annotation %q", spec[0])
key, val, hasVal := strings.Cut(annotationSpec, "=")
if !hasVal {
return "", fmt.Errorf("no value given for annotation %q", key)
}
annotations[spec[0]] = spec[1]
annotations[key] = val
}
opts.Annotations = envLib.Join(opts.Annotations, annotations)
}

View file

@ -27,64 +27,64 @@ func VolumeOptions(opts map[string]string) ([]libpod.VolumeCreateOption, error)
for _, o := range splitVal {
// Options will be formatted as either "opt" or
// "opt=value"
splitO := strings.SplitN(o, "=", 2)
switch strings.ToLower(splitO[0]) {
opt, val, hasVal := strings.Cut(o, "=")
switch strings.ToLower(opt) {
case "size":
size, err := units.FromHumanSize(splitO[1])
size, err := units.FromHumanSize(val)
if err != nil {
return nil, fmt.Errorf("cannot convert size %s to integer: %w", splitO[1], err)
return nil, fmt.Errorf("cannot convert size %s to integer: %w", val, err)
}
libpodOptions = append(libpodOptions, libpod.WithVolumeSize(uint64(size)))
finalVal = append(finalVal, o)
// set option "SIZE": "$size"
volumeOptions["SIZE"] = splitO[1]
volumeOptions["SIZE"] = val
case "inodes":
inodes, err := strconv.ParseUint(splitO[1], 10, 64)
inodes, err := strconv.ParseUint(val, 10, 64)
if err != nil {
return nil, fmt.Errorf("cannot convert inodes %s to integer: %w", splitO[1], err)
return nil, fmt.Errorf("cannot convert inodes %s to integer: %w", val, err)
}
libpodOptions = append(libpodOptions, libpod.WithVolumeInodes(inodes))
finalVal = append(finalVal, o)
// set option "INODES": "$size"
volumeOptions["INODES"] = splitO[1]
volumeOptions["INODES"] = val
case "uid":
if len(splitO) != 2 {
if !hasVal {
return nil, fmt.Errorf("uid option must provide a UID: %w", define.ErrInvalidArg)
}
intUID, err := strconv.Atoi(splitO[1])
intUID, err := strconv.Atoi(val)
if err != nil {
return nil, fmt.Errorf("cannot convert UID %s to integer: %w", splitO[1], err)
return nil, fmt.Errorf("cannot convert UID %s to integer: %w", val, err)
}
logrus.Debugf("Removing uid= from options and adding WithVolumeUID for UID %d", intUID)
libpodOptions = append(libpodOptions, libpod.WithVolumeUID(intUID), libpod.WithVolumeNoChown())
finalVal = append(finalVal, o)
// set option "UID": "$uid"
volumeOptions["UID"] = splitO[1]
volumeOptions["UID"] = val
case "gid":
if len(splitO) != 2 {
if !hasVal {
return nil, fmt.Errorf("gid option must provide a GID: %w", define.ErrInvalidArg)
}
intGID, err := strconv.Atoi(splitO[1])
intGID, err := strconv.Atoi(val)
if err != nil {
return nil, fmt.Errorf("cannot convert GID %s to integer: %w", splitO[1], err)
return nil, fmt.Errorf("cannot convert GID %s to integer: %w", val, err)
}
logrus.Debugf("Removing gid= from options and adding WithVolumeGID for GID %d", intGID)
libpodOptions = append(libpodOptions, libpod.WithVolumeGID(intGID), libpod.WithVolumeNoChown())
finalVal = append(finalVal, o)
// set option "GID": "$gid"
volumeOptions["GID"] = splitO[1]
volumeOptions["GID"] = val
case "noquota":
logrus.Debugf("Removing noquota from options and adding WithVolumeDisableQuota")
libpodOptions = append(libpodOptions, libpod.WithVolumeDisableQuota())
// set option "NOQUOTA": "true"
volumeOptions["NOQUOTA"] = "true"
case "timeout":
if len(splitO) != 2 {
if !hasVal {
return nil, fmt.Errorf("timeout option must provide a valid timeout in seconds: %w", define.ErrInvalidArg)
}
intTimeout, err := strconv.Atoi(splitO[1])
intTimeout, err := strconv.Atoi(val)
if err != nil {
return nil, fmt.Errorf("cannot convert Timeout %s to an integer: %w", splitO[1], err)
return nil, fmt.Errorf("cannot convert Timeout %s to an integer: %w", val, err)
}
if intTimeout < 0 {
return nil, fmt.Errorf("volume timeout cannot be negative (got %d)", intTimeout)

View file

@ -64,11 +64,11 @@ func (ir *ImageEngine) ManifestAdd(_ context.Context, name string, imageNames []
if len(opts.Annotation) != 0 {
annotations := make(map[string]string)
for _, annotationSpec := range opts.Annotation {
spec := strings.SplitN(annotationSpec, "=", 2)
if len(spec) != 2 {
return "", fmt.Errorf("no value given for annotation %q", spec[0])
key, val, hasVal := strings.Cut(annotationSpec, "=")
if !hasVal {
return "", fmt.Errorf("no value given for annotation %q", key)
}
annotations[spec[0]] = spec[1]
annotations[key] = val
}
opts.Annotations = envLib.Join(opts.Annotations, annotations)
}
@ -97,11 +97,11 @@ func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, name, images string
if len(opts.Annotation) != 0 {
annotations := make(map[string]string)
for _, annotationSpec := range opts.Annotation {
spec := strings.SplitN(annotationSpec, "=", 2)
if len(spec) != 2 {
return "", fmt.Errorf("no value given for annotation %q", spec[0])
key, val, hasVal := strings.Cut(annotationSpec, "=")
if !hasVal {
return "", fmt.Errorf("no value given for annotation %q", key)
}
annotations[spec[0]] = spec[1]
annotations[key] = val
}
opts.Annotations = envLib.Join(opts.Annotations, annotations)
}

View file

@ -406,8 +406,8 @@ func GetServiceInformation(sshInfo *entities.ImageScpConnections, cliConnections
var urlS string
var iden string
for i, val := range cliConnections {
splitEnv := strings.SplitN(val, "::", 2)
sshInfo.Connections = append(sshInfo.Connections, splitEnv[0])
connection, _, _ := strings.Cut(val, "::")
sshInfo.Connections = append(sshInfo.Connections, connection)
conn, found := cfg.Engine.ServiceDestinations[sshInfo.Connections[i]]
if found {
urlS = conn.URI

View file

@ -34,8 +34,8 @@ func ToLibpodFilters(f url.Values) (filters []string) {
func ToURLValues(f []string) (filters url.Values) {
filters = make(url.Values)
for _, v := range f {
t := strings.SplitN(v, "=", 2)
filters.Add(t[0], t[1])
key, val, _ := strings.Cut(v, "=")
filters.Add(key, val)
}
return
}

32
pkg/env/env.go vendored
View file

@ -40,14 +40,9 @@ func Slice(m map[string]string) []string {
// map.
func Map(slice []string) map[string]string {
envmap := make(map[string]string, len(slice))
for _, val := range slice {
data := strings.SplitN(val, "=", 2)
if len(data) > 1 {
envmap[data[0]] = data[1]
} else {
envmap[data[0]] = ""
}
for _, line := range slice {
key, val, _ := strings.Cut(line, "=")
envmap[key] = val
}
return envmap
}
@ -94,26 +89,25 @@ func ParseFile(path string) (_ map[string]string, err error) {
}
func parseEnv(env map[string]string, line string) error {
data := strings.SplitN(line, "=", 2)
key, val, hasVal := strings.Cut(line, "=")
// catch invalid variables such as "=" or "=A"
if data[0] == "" {
if key == "" {
return fmt.Errorf("invalid variable: %q", line)
}
// trim the front of a variable, but nothing else
name := strings.TrimLeft(data[0], whiteSpaces)
if len(data) > 1 {
env[name] = data[1]
name := strings.TrimLeft(key, whiteSpaces)
if hasVal {
env[name] = val
} else {
if strings.HasSuffix(name, "*") {
name = strings.TrimSuffix(name, "*")
if name, hasStar := strings.CutSuffix(name, "*"); hasStar {
for _, e := range os.Environ() {
part := strings.SplitN(e, "=", 2)
if len(part) < 2 {
envKey, envVal, hasEq := strings.Cut(e, "=")
if !hasEq {
continue
}
if strings.HasPrefix(part[0], name) {
env[part[0]] = part[1]
if strings.HasPrefix(envKey, name) {
env[envKey] = envVal
}
}
} else if val, ok := os.LookupEnv(name); ok {

View file

@ -75,8 +75,8 @@ func TestPropagateHostEnv(t *testing.T) {
// envs looks like: {"BAR": "bar", "FOO": "foo"}
envs := make(map[string]string)
for _, env := range envsRawArr {
item := strings.SplitN(env, "=", 2)
envs[item[0]] = strings.Trim(item[1], "\"")
key, value, _ := strings.Cut(env, "=")
envs[key] = strings.Trim(value, "\"")
}
for key, test := range tests {

View file

@ -48,24 +48,21 @@ func (n CgroupMode) IsNS() bool {
// NS gets the path associated with a ns:<path> cgroup ns
func (n CgroupMode) NS() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 {
return parts[1]
}
return ""
_, path, _ := strings.Cut(string(n), ":")
return path
}
// IsContainer indicates whether the container uses a new cgroup namespace.
func (n CgroupMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == containerType
typ, _, hasColon := strings.Cut(string(n), ":")
return hasColon && typ == containerType
}
// Container returns the name of the container whose cgroup namespace is going to be used.
func (n CgroupMode) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 && parts[0] == containerType {
return parts[1]
typ, name, hasName := strings.Cut(string(n), ":")
if hasName && typ == containerType {
return name
}
return ""
}
@ -123,36 +120,36 @@ func (n UsernsMode) IsDefaultValue() bool {
// GetKeepIDOptions returns a KeepIDUserNsOptions with the settings to keepIDmatically set up
// a user namespace.
func (n UsernsMode) GetKeepIDOptions() (*KeepIDUserNsOptions, error) {
parts := strings.SplitN(string(n), ":", 2)
if parts[0] != "keep-id" {
nsmode, nsopts, hasOpts := strings.Cut(string(n), ":")
if nsmode != "keep-id" {
return nil, fmt.Errorf("wrong user namespace mode")
}
options := KeepIDUserNsOptions{}
if len(parts) == 1 {
if !hasOpts {
return &options, nil
}
for _, o := range strings.Split(parts[1], ",") {
v := strings.SplitN(o, "=", 2)
if len(v) != 2 {
for _, o := range strings.Split(nsopts, ",") {
opt, val, hasVal := strings.Cut(o, "=")
if !hasVal {
return nil, fmt.Errorf("invalid option specified: %q", o)
}
switch v[0] {
switch opt {
case "uid":
s, err := strconv.ParseUint(v[1], 10, 32)
s, err := strconv.ParseUint(val, 10, 32)
if err != nil {
return nil, err
}
v := uint32(s)
options.UID = &v
case "gid":
s, err := strconv.ParseUint(v[1], 10, 32)
s, err := strconv.ParseUint(val, 10, 32)
if err != nil {
return nil, err
}
v := uint32(s)
options.GID = &v
default:
return nil, fmt.Errorf("unknown option specified: %q", v[0])
return nil, fmt.Errorf("unknown option specified: %q", opt)
}
}
return &options, nil
@ -185,24 +182,21 @@ func (n UsernsMode) IsNS() bool {
// NS gets the path associated with a ns:<path> userns ns
func (n UsernsMode) NS() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 {
return parts[1]
}
return ""
_, path, _ := strings.Cut(string(n), ":")
return path
}
// IsContainer indicates whether container uses a container userns.
func (n UsernsMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == containerType
typ, _, hasName := strings.Cut(string(n), ":")
return hasName && typ == containerType
}
// Container is the id of the container which network this container is connected to.
func (n UsernsMode) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 && parts[0] == containerType {
return parts[1]
typ, name, hasName := strings.Cut(string(n), ":")
if hasName && typ == containerType {
return name
}
return ""
}
@ -222,15 +216,15 @@ func (n UTSMode) IsHost() bool {
// IsContainer indicates whether the container uses a container's UTS namespace.
func (n UTSMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == containerType
typ, _, hasName := strings.Cut(string(n), ":")
return hasName && typ == containerType
}
// Container returns the name of the container whose uts namespace is going to be used.
func (n UTSMode) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 && parts[0] == containerType {
return parts[1]
typ, name, hasName := strings.Cut(string(n), ":")
if hasName && typ == containerType {
return name
}
return ""
}
@ -270,8 +264,8 @@ func (n IpcMode) IsShareable() bool {
// IsContainer indicates whether the container uses another container's ipc namespace.
func (n IpcMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == containerType
typ, _, hasName := strings.Cut(string(n), ":")
return hasName && typ == containerType
}
// IsNone indicates whether container IpcMode is set to "none".
@ -291,9 +285,9 @@ func (n IpcMode) Valid() bool {
// Container returns the name of the container ipc stack is going to be used.
func (n IpcMode) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 && parts[0] == containerType {
return parts[1]
typ, name, hasName := strings.Cut(string(n), ":")
if hasName && typ == containerType {
return name
}
return ""
}
@ -313,8 +307,8 @@ func (n PidMode) IsHost() bool {
// IsContainer indicates whether the container uses a container's pid namespace.
func (n PidMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == containerType
typ, _, hasName := strings.Cut(string(n), ":")
return hasName && typ == containerType
}
// Valid indicates whether the pid namespace is valid.
@ -334,9 +328,9 @@ func (n PidMode) Valid() bool {
// Container returns the name of the container whose pid namespace is going to be used.
func (n PidMode) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 && parts[0] == containerType {
return parts[1]
typ, name, hasName := strings.Cut(string(n), ":")
if hasName && typ == containerType {
return name
}
return ""
}
@ -366,15 +360,15 @@ func (n NetworkMode) IsPrivate() bool {
// IsContainer indicates whether container uses a container network stack.
func (n NetworkMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == containerType
typ, _, hasName := strings.Cut(string(n), ":")
return hasName && typ == containerType
}
// Container is the id of the container which network this container is connected to.
func (n NetworkMode) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 && parts[0] == containerType {
return parts[1]
typ, name, hasName := strings.Cut(string(n), ":")
if hasName && typ == containerType {
return name
}
return ""
}
@ -409,11 +403,8 @@ func (n NetworkMode) IsNS() bool {
// NS gets the path associated with a ns:<path> network ns
func (n NetworkMode) NS() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 {
return parts[1]
}
return ""
_, path, _ := strings.Cut(string(n), ":")
return path
}
// IsPod returns whether the network refers to pod networking

View file

@ -31,9 +31,8 @@ func GetRacct(filter string) (map[string]uint64, error) {
entries := strings.Split(string(buf[:len]), ",")
res := make(map[string]uint64)
for _, entry := range entries {
kv := strings.SplitN(entry, "=", 2)
key := kv[0]
val, err := strconv.ParseUint(kv[1], 10, 0)
key, valstr, _ := strings.Cut(entry, "=")
val, err := strconv.ParseUint(valstr, 10, 0)
if err != nil {
logrus.Warnf("unexpected rctl entry, ignoring: %s", entry)
}

View file

@ -236,13 +236,8 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat
}
}
for _, v := range rtc.Containers.Annotations.Get() {
split := strings.SplitN(v, "=", 2)
k := split[0]
v := ""
if len(split) == 2 {
v = split[1]
}
for _, annotation := range rtc.Containers.Annotations.Get() {
k, v, _ := strings.Cut(annotation, "=")
annotations[k] = v
}
// now pass in the values from client
@ -356,9 +351,9 @@ func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, containerID
if conf.Spec.Process != nil && conf.Spec.Process.Env != nil {
env := make(map[string]string)
for _, entry := range conf.Spec.Process.Env {
split := strings.SplitN(entry, "=", 2)
if len(split) == 2 {
env[split[0]] = split[1]
key, val, hasVal := strings.Cut(entry, "=")
if hasVal {
env[key] = val
}
}
specg.Env = env

View file

@ -54,12 +54,12 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
s.ResourceLimits.Unified = make(map[string]string)
}
for _, cgroupConf := range rtc.Containers.CgroupConf.Get() {
cgr := strings.SplitN(cgroupConf, "=", 2)
if len(cgr) != 2 {
return nil, nil, nil, fmt.Errorf("CgroupConf %q from containers.conf invalid, must be name=value", cgr)
key, val, hasVal := strings.Cut(cgroupConf, "=")
if !hasVal {
return nil, nil, nil, fmt.Errorf("CgroupConf %s from containers.conf invalid, must be name=value", cgroupConf)
}
if _, ok := s.ResourceLimits.Unified[cgr[0]]; !ok {
s.ResourceLimits.Unified[cgr[0]] = cgr[1]
if _, ok := s.ResourceLimits.Unified[key]; !ok {
s.ResourceLimits.Unified[key] = val
}
}
}

View file

@ -221,29 +221,29 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener
s.LogConfiguration.Options = make(map[string]string)
for _, o := range opts.LogOptions {
split := strings.SplitN(o, "=", 2)
if len(split) < 2 {
opt, val, hasVal := strings.Cut(o, "=")
if !hasVal {
return nil, fmt.Errorf("invalid log option %q", o)
}
switch strings.ToLower(split[0]) {
switch strings.ToLower(opt) {
case "driver":
s.LogConfiguration.Driver = split[1]
s.LogConfiguration.Driver = val
case "path":
s.LogConfiguration.Path = split[1]
s.LogConfiguration.Path = val
case "max-size":
logSize, err := units.FromHumanSize(split[1])
logSize, err := units.FromHumanSize(val)
if err != nil {
return nil, err
}
s.LogConfiguration.Size = logSize
default:
switch len(split[1]) {
switch len(val) {
case 0:
return nil, fmt.Errorf("invalid log option: %w", define.ErrInvalidArg)
default:
// tags for journald only
if s.LogConfiguration.Driver == "" || s.LogConfiguration.Driver == define.JournaldLogging {
s.LogConfiguration.Options[split[0]] = split[1]
s.LogConfiguration.Options[opt] = val
} else {
logrus.Warnf("Can only set tags with journald log driver but driver is %q", s.LogConfiguration.Driver)
}
@ -434,8 +434,8 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener
// Environment Variables
envs := map[string]string{}
for _, env := range imageData.Config.Env {
keyval := strings.SplitN(env, "=", 2)
envs[keyval[0]] = keyval[1]
key, val, _ := strings.Cut(env, "=")
envs[key] = val
}
for _, env := range opts.Container.Env {

View file

@ -262,9 +262,9 @@ func getVolumesFrom(volumesFrom []string, runtime *libpod.Runtime) (map[string]s
for _, volume := range volumesFrom {
var options []string
splitVol := strings.SplitN(volume, ":", 2)
if len(splitVol) == 2 {
splitOpts := strings.Split(splitVol[1], ",")
idOrName, volOpts, hasVolOpts := strings.Cut(volume, ":")
if hasVolOpts {
splitOpts := strings.Split(volOpts, ",")
setRORW := false
setZ := false
for _, opt := range splitOpts {
@ -286,9 +286,9 @@ func getVolumesFrom(volumesFrom []string, runtime *libpod.Runtime) (map[string]s
options = splitOpts
}
ctr, err := runtime.LookupContainer(splitVol[0])
ctr, err := runtime.LookupContainer(idOrName)
if err != nil {
return nil, nil, fmt.Errorf("looking up container %q for volumes-from: %w", splitVol[0], err)
return nil, nil, fmt.Errorf("looking up container %q for volumes-from: %w", idOrName, err)
}
logrus.Debugf("Adding volumes from container %s", ctr.ID())

View file

@ -230,29 +230,23 @@ func (n *Namespace) validate() error {
// function.
func ParseNamespace(ns string) (Namespace, error) {
toReturn := Namespace{}
switch {
case ns == "pod":
switch ns {
case "pod":
toReturn.NSMode = FromPod
case ns == "host":
case "host":
toReturn.NSMode = Host
case ns == "private", ns == "":
case "private", "":
toReturn.NSMode = Private
case strings.HasPrefix(ns, "ns:"):
split := strings.SplitN(ns, ":", 2)
if len(split) != 2 {
return toReturn, fmt.Errorf("must provide a path to a namespace when specifying \"ns:\"")
}
toReturn.NSMode = Path
toReturn.Value = split[1]
case strings.HasPrefix(ns, "container:"):
split := strings.SplitN(ns, ":", 2)
if len(split) != 2 {
return toReturn, fmt.Errorf("must provide name or ID or a container when specifying \"container:\"")
}
toReturn.NSMode = FromContainer
toReturn.Value = split[1]
default:
return toReturn, fmt.Errorf("unrecognized namespace mode %s passed", ns)
if value, ok := strings.CutPrefix(ns, "ns:"); ok {
toReturn.NSMode = Path
toReturn.Value = value
} else if value, ok := strings.CutPrefix(ns, "container:"); ok {
toReturn.NSMode = FromContainer
toReturn.Value = value
} else {
return toReturn, fmt.Errorf("unrecognized namespace mode %s passed", ns)
}
}
return toReturn, nil
@ -302,37 +296,32 @@ func ParseIPCNamespace(ns string) (Namespace, error) {
// form.
func ParseUserNamespace(ns string) (Namespace, error) {
toReturn := Namespace{}
switch {
case ns == "auto":
switch ns {
case "auto":
toReturn.NSMode = Auto
return toReturn, nil
case strings.HasPrefix(ns, "auto:"):
split := strings.SplitN(ns, ":", 2)
if len(split) != 2 {
return toReturn, errors.New("invalid setting for auto: mode")
}
toReturn.NSMode = Auto
toReturn.Value = split[1]
return toReturn, nil
case ns == "keep-id":
case "keep-id":
toReturn.NSMode = KeepID
return toReturn, nil
case strings.HasPrefix(ns, "keep-id:"):
split := strings.SplitN(ns, ":", 2)
if len(split) != 2 {
return toReturn, errors.New("invalid setting for keep-id: mode")
}
toReturn.NSMode = KeepID
toReturn.Value = split[1]
return toReturn, nil
case ns == "nomap":
case "nomap":
toReturn.NSMode = NoMap
return toReturn, nil
case ns == "":
case "":
toReturn.NSMode = Host
return toReturn, nil
default:
if value, ok := strings.CutPrefix(ns, "auto:"); ok {
toReturn.NSMode = Auto
toReturn.Value = value
return toReturn, nil
} else if value, ok := strings.CutPrefix(ns, "keep-id:"); ok {
toReturn.NSMode = KeepID
toReturn.Value = value
return toReturn, nil
} else {
return ParseNamespace(ns)
}
}
return ParseNamespace(ns)
}
// ParseNetworkFlag parses a network string slice into the network options
@ -352,10 +341,10 @@ func ParseNetworkFlag(networks []string, pastaNetworkNameExists bool) (Namespace
switch {
case ns == string(Slirp), strings.HasPrefix(ns, string(Slirp)+":"):
parts := strings.SplitN(ns, ":", 2)
if len(parts) > 1 {
key, options, hasOptions := strings.Cut(ns, ":")
if hasOptions {
networkOptions = make(map[string][]string)
networkOptions[parts[0]] = strings.Split(parts[1], ",")
networkOptions[key] = strings.Split(options, ",")
}
toReturn.NSMode = Slirp
case ns == string(FromPod):
@ -364,11 +353,11 @@ func ParseNetworkFlag(networks []string, pastaNetworkNameExists bool) (Namespace
toReturn.NSMode = Private
case ns == string(Bridge), strings.HasPrefix(ns, string(Bridge)+":"):
toReturn.NSMode = Bridge
parts := strings.SplitN(ns, ":", 2)
_, options, hasOptions := strings.Cut(ns, ":")
netOpts := types.PerNetworkOptions{}
if len(parts) > 1 {
if hasOptions {
var err error
netOpts, err = parseBridgeNetworkOptions(parts[1])
netOpts, err = parseBridgeNetworkOptions(options)
if err != nil {
return toReturn, nil, nil, err
}
@ -381,30 +370,23 @@ func ParseNetworkFlag(networks []string, pastaNetworkNameExists bool) (Namespace
case ns == string(Host):
toReturn.NSMode = Host
case strings.HasPrefix(ns, "ns:"):
split := strings.SplitN(ns, ":", 2)
if len(split) != 2 {
return toReturn, nil, nil, errors.New("must provide a path to a namespace when specifying \"ns:\"")
}
_, value, _ := strings.Cut(ns, ":")
toReturn.NSMode = Path
toReturn.Value = split[1]
toReturn.Value = value
case strings.HasPrefix(ns, string(FromContainer)+":"):
split := strings.SplitN(ns, ":", 2)
if len(split) != 2 {
return toReturn, nil, nil, errors.New("must provide name or ID or a container when specifying \"container:\"")
}
_, value, _ := strings.Cut(ns, ":")
toReturn.NSMode = FromContainer
toReturn.Value = split[1]
toReturn.Value = value
case ns == string(Pasta), strings.HasPrefix(ns, string(Pasta)+":"):
var parts []string
key, options, hasOptions := strings.Cut(ns, ":")
if pastaNetworkNameExists {
goto nextCase
}
parts = strings.SplitN(ns, ":", 2)
if len(parts) > 1 {
if hasOptions {
networkOptions = make(map[string][]string)
networkOptions[parts[0]] = strings.Split(parts[1], ",")
networkOptions[key] = strings.Split(options, ",")
}
toReturn.NSMode = Pasta
break
@ -412,22 +394,22 @@ func ParseNetworkFlag(networks []string, pastaNetworkNameExists bool) (Namespace
fallthrough
default:
// we should have a normal network
parts := strings.SplitN(ns, ":", 2)
if len(parts) == 1 {
name, options, hasOptions := strings.Cut(ns, ":")
if hasOptions {
if name == "" {
return toReturn, nil, nil, errors.New("network name cannot be empty")
}
netOpts, err := parseBridgeNetworkOptions(options)
if err != nil {
return toReturn, nil, nil, fmt.Errorf("invalid option for network %s: %w", name, err)
}
podmanNetworks[name] = netOpts
} else {
// Assume we have been given a comma separated list of networks for backwards compat.
networkList := strings.Split(ns, ",")
for _, net := range networkList {
podmanNetworks[net] = types.PerNetworkOptions{}
}
} else {
if parts[0] == "" {
return toReturn, nil, nil, errors.New("network name cannot be empty")
}
netOpts, err := parseBridgeNetworkOptions(parts[1])
if err != nil {
return toReturn, nil, nil, fmt.Errorf("invalid option for network %s: %w", parts[0], err)
}
podmanNetworks[parts[0]] = netOpts
}
// networks need bridge mode
@ -440,24 +422,24 @@ func ParseNetworkFlag(networks []string, pastaNetworkNameExists bool) (Namespace
}
for _, network := range networks[1:] {
parts := strings.SplitN(network, ":", 2)
if parts[0] == "" {
name, options, hasOptions := strings.Cut(network, ":")
if name == "" {
return toReturn, nil, nil, fmt.Errorf("network name cannot be empty: %w", define.ErrInvalidArg)
}
// TODO (5.0): Don't accept string(Pasta) here once we drop pastaNetworkNameExists
if slices.Contains([]string{string(Bridge), string(Slirp), string(FromPod), string(NoNetwork),
string(Default), string(Private), string(Path), string(FromContainer), string(Host)}, parts[0]) {
return toReturn, nil, nil, fmt.Errorf("can only set extra network names, selected mode %s conflicts with bridge: %w", parts[0], define.ErrInvalidArg)
string(Default), string(Private), string(Path), string(FromContainer), string(Host)}, name) {
return toReturn, nil, nil, fmt.Errorf("can only set extra network names, selected mode %s conflicts with bridge: %w", name, define.ErrInvalidArg)
}
netOpts := types.PerNetworkOptions{}
if len(parts) > 1 {
if hasOptions {
var err error
netOpts, err = parseBridgeNetworkOptions(parts[1])
netOpts, err = parseBridgeNetworkOptions(options)
if err != nil {
return toReturn, nil, nil, fmt.Errorf("invalid option for network %s: %w", parts[0], err)
return toReturn, nil, nil, fmt.Errorf("invalid option for network %s: %w", name, err)
}
}
podmanNetworks[parts[0]] = netOpts
podmanNetworks[name] = netOpts
}
}
@ -471,36 +453,36 @@ func parseBridgeNetworkOptions(opts string) (types.PerNetworkOptions, error) {
}
allopts := strings.Split(opts, ",")
for _, opt := range allopts {
split := strings.SplitN(opt, "=", 2)
switch split[0] {
name, value, _ := strings.Cut(opt, "=")
switch name {
case "ip", "ip6":
ip := net.ParseIP(split[1])
ip := net.ParseIP(value)
if ip == nil {
return netOpts, fmt.Errorf("invalid ip address %q", split[1])
return netOpts, fmt.Errorf("invalid ip address %q", value)
}
netOpts.StaticIPs = append(netOpts.StaticIPs, ip)
case "mac":
mac, err := net.ParseMAC(split[1])
mac, err := net.ParseMAC(value)
if err != nil {
return netOpts, err
}
netOpts.StaticMAC = types.HardwareAddr(mac)
case "alias":
if split[1] == "" {
if value == "" {
return netOpts, errors.New("alias cannot be empty")
}
netOpts.Aliases = append(netOpts.Aliases, split[1])
netOpts.Aliases = append(netOpts.Aliases, value)
case "interface_name":
if split[1] == "" {
if value == "" {
return netOpts, errors.New("interface_name cannot be empty")
}
netOpts.InterfaceName = split[1]
netOpts.InterfaceName = value
default:
return netOpts, fmt.Errorf("unknown bridge network option: %s", split[0])
return netOpts, fmt.Errorf("unknown bridge network option: %s", name)
}
}
return netOpts, nil

View file

@ -502,11 +502,11 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
// Last, add user annotations
for _, annotation := range c.Annotation {
splitAnnotation := strings.SplitN(annotation, "=", 2)
if len(splitAnnotation) < 2 {
key, val, hasVal := strings.Cut(annotation, "=")
if !hasVal {
return errors.New("annotations must be formatted KEY=VALUE")
}
annotations[splitAnnotation[0]] = splitAnnotation[1]
annotations[key] = val
}
if len(s.Annotations) == 0 {
s.Annotations = annotations
@ -515,11 +515,11 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
if len(c.StorageOpts) > 0 {
opts := make(map[string]string, len(c.StorageOpts))
for _, opt := range c.StorageOpts {
split := strings.SplitN(opt, "=", 2)
if len(split) != 2 {
key, val, hasVal := strings.Cut(opt, "=")
if !hasVal {
return errors.New("storage-opt must be formatted KEY=VALUE")
}
opts[split[0]] = split[1]
opts[key] = val
}
s.StorageOpts = opts
}
@ -673,11 +673,11 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
// check if key=value and convert
sysmap := make(map[string]string)
for _, ctl := range c.Sysctl {
splitCtl := strings.SplitN(ctl, "=", 2)
if len(splitCtl) < 2 {
key, val, hasVal := strings.Cut(ctl, "=")
if !hasVal {
return fmt.Errorf("invalid sysctl value %q", ctl)
}
sysmap[splitCtl[0]] = splitCtl[1]
sysmap[key] = val
}
if len(s.Sysctl) == 0 || len(c.Sysctl) != 0 {
s.Sysctl = sysmap
@ -690,48 +690,51 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
for _, opt := range c.SecurityOpt {
// Docker deprecated the ":" syntax but still supports it,
// so we need to as well
var con []string
var key, val string
var hasVal bool
if strings.Contains(opt, "=") {
con = strings.SplitN(opt, "=", 2)
key, val, hasVal = strings.Cut(opt, "=")
} else {
con = strings.SplitN(opt, ":", 2)
key, val, hasVal = strings.Cut(opt, ":")
}
if len(con) != 2 &&
con[0] != "no-new-privileges" {
if !hasVal &&
key != "no-new-privileges" {
return fmt.Errorf("invalid --security-opt 1: %q", opt)
}
switch con[0] {
switch key {
case "apparmor":
s.ContainerSecurityConfig.ApparmorProfile = con[1]
s.Annotations[define.InspectAnnotationApparmor] = con[1]
s.ContainerSecurityConfig.ApparmorProfile = val
s.Annotations[define.InspectAnnotationApparmor] = val
case "label":
if con[1] == "nested" {
if val == "nested" {
s.ContainerSecurityConfig.LabelNested = true
continue
}
// TODO selinux opts and label opts are the same thing
s.ContainerSecurityConfig.SelinuxOpts = append(s.ContainerSecurityConfig.SelinuxOpts, con[1])
s.ContainerSecurityConfig.SelinuxOpts = append(s.ContainerSecurityConfig.SelinuxOpts, val)
s.Annotations[define.InspectAnnotationLabel] = strings.Join(s.ContainerSecurityConfig.SelinuxOpts, ",label=")
case "mask":
s.ContainerSecurityConfig.Mask = append(s.ContainerSecurityConfig.Mask, strings.Split(con[1], ":")...)
s.ContainerSecurityConfig.Mask = append(s.ContainerSecurityConfig.Mask, strings.Split(val, ":")...)
case "proc-opts":
s.ProcOpts = strings.Split(con[1], ",")
s.ProcOpts = strings.Split(val, ",")
case "seccomp":
s.SeccompProfilePath = con[1]
s.Annotations[define.InspectAnnotationSeccomp] = con[1]
s.SeccompProfilePath = val
s.Annotations[define.InspectAnnotationSeccomp] = val
// this option is for docker compatibility, it is the same as unmask=ALL
case "systempaths":
if con[1] == "unconfined" {
if val == "unconfined" {
s.ContainerSecurityConfig.Unmask = append(s.ContainerSecurityConfig.Unmask, []string{"ALL"}...)
} else {
return fmt.Errorf("invalid systempaths option %q, only `unconfined` is supported", con[1])
return fmt.Errorf("invalid systempaths option %q, only `unconfined` is supported", val)
}
case "unmask":
s.ContainerSecurityConfig.Unmask = append(s.ContainerSecurityConfig.Unmask, con[1:]...)
if hasVal {
s.ContainerSecurityConfig.Unmask = append(s.ContainerSecurityConfig.Unmask, val)
}
case "no-new-privileges":
noNewPrivileges := true
if len(con) == 2 {
noNewPrivileges, err = strconv.ParseBool(con[1])
if hasVal {
noNewPrivileges, err = strconv.ParseBool(val)
if err != nil {
return fmt.Errorf("invalid --security-opt 2: %q", opt)
}
@ -813,23 +816,23 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
logOpts := make(map[string]string)
for _, o := range c.LogOptions {
split := strings.SplitN(o, "=", 2)
if len(split) < 2 {
key, val, hasVal := strings.Cut(o, "=")
if !hasVal {
return fmt.Errorf("invalid log option %q", o)
}
switch strings.ToLower(split[0]) {
switch strings.ToLower(key) {
case "driver":
s.LogConfiguration.Driver = split[1]
s.LogConfiguration.Driver = val
case "path":
s.LogConfiguration.Path = split[1]
s.LogConfiguration.Path = val
case "max-size":
logSize, err := units.FromHumanSize(split[1])
logSize, err := units.FromHumanSize(val)
if err != nil {
return err
}
s.LogConfiguration.Size = logSize
default:
logOpts[split[0]] = split[1]
logOpts[key] = val
}
}
if len(s.LogConfiguration.Options) == 0 || len(c.LogOptions) != 0 {
@ -1004,23 +1007,23 @@ func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, start
func parseWeightDevices(weightDevs []string) (map[string]specs.LinuxWeightDevice, error) {
wd := make(map[string]specs.LinuxWeightDevice)
for _, val := range weightDevs {
split := strings.SplitN(val, ":", 2)
if len(split) != 2 {
return nil, fmt.Errorf("bad format: %s", val)
for _, dev := range weightDevs {
key, val, hasVal := strings.Cut(dev, ":")
if !hasVal {
return nil, fmt.Errorf("bad format: %s", dev)
}
if !strings.HasPrefix(split[0], "/dev/") {
return nil, fmt.Errorf("bad format for device path: %s", val)
if !strings.HasPrefix(key, "/dev/") {
return nil, fmt.Errorf("bad format for device path: %s", dev)
}
weight, err := strconv.ParseUint(split[1], 10, 0)
weight, err := strconv.ParseUint(val, 10, 0)
if err != nil {
return nil, fmt.Errorf("invalid weight for device: %s", val)
return nil, fmt.Errorf("invalid weight for device: %s", dev)
}
if weight > 0 && (weight < 10 || weight > 1000) {
return nil, fmt.Errorf("invalid weight for device: %s", val)
return nil, fmt.Errorf("invalid weight for device: %s", dev)
}
w := uint16(weight)
wd[split[0]] = specs.LinuxWeightDevice{
wd[key] = specs.LinuxWeightDevice{
Weight: &w,
LeafWeight: nil,
}
@ -1030,41 +1033,41 @@ func parseWeightDevices(weightDevs []string) (map[string]specs.LinuxWeightDevice
func parseThrottleBPSDevices(bpsDevices []string) (map[string]specs.LinuxThrottleDevice, error) {
td := make(map[string]specs.LinuxThrottleDevice)
for _, val := range bpsDevices {
split := strings.SplitN(val, ":", 2)
if len(split) != 2 {
return nil, fmt.Errorf("bad format: %s", val)
for _, dev := range bpsDevices {
key, val, hasVal := strings.Cut(dev, ":")
if !hasVal {
return nil, fmt.Errorf("bad format: %s", dev)
}
if !strings.HasPrefix(split[0], "/dev/") {
return nil, fmt.Errorf("bad format for device path: %s", val)
if !strings.HasPrefix(key, "/dev/") {
return nil, fmt.Errorf("bad format for device path: %s", dev)
}
rate, err := units.RAMInBytes(split[1])
rate, err := units.RAMInBytes(val)
if err != nil {
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", dev)
}
if rate < 0 {
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", dev)
}
td[split[0]] = specs.LinuxThrottleDevice{Rate: uint64(rate)}
td[key] = specs.LinuxThrottleDevice{Rate: uint64(rate)}
}
return td, nil
}
func parseThrottleIOPsDevices(iopsDevices []string) (map[string]specs.LinuxThrottleDevice, error) {
td := make(map[string]specs.LinuxThrottleDevice)
for _, val := range iopsDevices {
split := strings.SplitN(val, ":", 2)
if len(split) != 2 {
return nil, fmt.Errorf("bad format: %s", val)
for _, dev := range iopsDevices {
key, val, hasVal := strings.Cut(dev, ":")
if !hasVal {
return nil, fmt.Errorf("bad format: %s", dev)
}
if !strings.HasPrefix(split[0], "/dev/") {
return nil, fmt.Errorf("bad format for device path: %s", val)
if !strings.HasPrefix(key, "/dev/") {
return nil, fmt.Errorf("bad format for device path: %s", dev)
}
rate, err := strconv.ParseUint(split[1], 10, 64)
rate, err := strconv.ParseUint(val, 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>. Number must be a positive integer", val)
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>. Number must be a positive integer", dev)
}
td[split[0]] = specs.LinuxThrottleDevice{Rate: rate}
td[key] = specs.LinuxThrottleDevice{Rate: rate}
}
return td, nil
}
@ -1103,42 +1106,42 @@ func parseSecrets(secrets []string) ([]specgen.Secret, map[string]string, error)
}
for _, val := range split {
kv := strings.SplitN(val, "=", 2)
if len(kv) < 2 {
name, value, hasValue := strings.Cut(val, "=")
if !hasValue {
return nil, nil, fmt.Errorf("option %s must be in form option=value: %w", val, secretParseError)
}
switch kv[0] {
switch name {
case "source":
source = kv[1]
source = value
case "type":
if secretType != "" {
return nil, nil, fmt.Errorf("cannot set more than one secret type: %w", secretParseError)
}
if kv[1] != "mount" && kv[1] != "env" {
return nil, nil, fmt.Errorf("type %s is invalid: %w", kv[1], secretParseError)
if value != "mount" && value != "env" {
return nil, nil, fmt.Errorf("type %s is invalid: %w", value, secretParseError)
}
secretType = kv[1]
secretType = value
case "target":
target = kv[1]
target = value
case "mode":
mountOnly = true
mode64, err := strconv.ParseUint(kv[1], 8, 32)
mode64, err := strconv.ParseUint(value, 8, 32)
if err != nil {
return nil, nil, fmt.Errorf("mode %s invalid: %w", kv[1], secretParseError)
return nil, nil, fmt.Errorf("mode %s invalid: %w", value, secretParseError)
}
mode = uint32(mode64)
case "uid", "UID":
mountOnly = true
uid64, err := strconv.ParseUint(kv[1], 10, 32)
uid64, err := strconv.ParseUint(value, 10, 32)
if err != nil {
return nil, nil, fmt.Errorf("UID %s invalid: %w", kv[1], secretParseError)
return nil, nil, fmt.Errorf("UID %s invalid: %w", value, secretParseError)
}
uid = uint32(uid64)
case "gid", "GID":
mountOnly = true
gid64, err := strconv.ParseUint(kv[1], 10, 32)
gid64, err := strconv.ParseUint(value, 10, 32)
if err != nil {
return nil, nil, fmt.Errorf("GID %s invalid: %w", kv[1], secretParseError)
return nil, nil, fmt.Errorf("GID %s invalid: %w", value, secretParseError)
}
gid = uint32(gid64)
@ -1203,17 +1206,17 @@ func parseLinuxResourcesDeviceAccess(device string) (specs.LinuxDeviceCgroup, er
return specs.LinuxDeviceCgroup{}, fmt.Errorf("invalid device type in device-access-add: %s", devType)
}
number := strings.SplitN(value[1], ":", 2)
if number[0] != "*" {
i, err := strconv.ParseUint(number[0], 10, 64)
majorNumber, minorNumber, hasMinor := strings.Cut(value[1], ":")
if majorNumber != "*" {
i, err := strconv.ParseUint(majorNumber, 10, 64)
if err != nil {
return specs.LinuxDeviceCgroup{}, err
}
m := int64(i)
major = &m
}
if len(number) == 2 && number[1] != "*" {
i, err := strconv.ParseUint(number[1], 10, 64)
if hasMinor && minorNumber != "*" {
i, err := strconv.ParseUint(minorNumber, 10, 64)
if err != nil {
return specs.LinuxDeviceCgroup{}, err
}
@ -1263,11 +1266,11 @@ func GetResources(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions)
unifieds := make(map[string]string)
for _, unified := range c.CgroupConf {
splitUnified := strings.SplitN(unified, "=", 2)
if len(splitUnified) < 2 {
key, val, hasVal := strings.Cut(unified, "=")
if !hasVal {
return nil, errors.New("--cgroup-conf must be formatted KEY=VALUE")
}
unifieds[splitUnified[0]] = splitUnified[1]
unifieds[key] = val
}
if len(unifieds) > 0 {
s.ResourceLimits.Unified = unifieds

View file

@ -256,29 +256,29 @@ func parseMountOptions(mountType string, args []string) (*spec.Mount, error) {
var setTmpcopyup, setRORW, setSuid, setDev, setExec, setRelabel, setOwnership, setSwap bool
mnt := spec.Mount{}
for _, val := range args {
kv := strings.SplitN(val, "=", 2)
switch kv[0] {
for _, arg := range args {
name, value, hasValue := strings.Cut(arg, "=")
switch name {
case "bind-nonrecursive":
if mountType != define.TypeBind {
return nil, fmt.Errorf("%q option not supported for %q mount types", kv[0], mountType)
return nil, fmt.Errorf("%q option not supported for %q mount types", name, mountType)
}
mnt.Options = append(mnt.Options, define.TypeBind)
case "bind-propagation":
if mountType != define.TypeBind {
return nil, fmt.Errorf("%q option not supported for %q mount types", kv[0], mountType)
return nil, fmt.Errorf("%q option not supported for %q mount types", name, mountType)
}
if len(kv) == 1 {
return nil, fmt.Errorf("%v: %w", kv[0], errOptionArg)
if !hasValue {
return nil, fmt.Errorf("%v: %w", name, errOptionArg)
}
mnt.Options = append(mnt.Options, kv[1])
mnt.Options = append(mnt.Options, value)
case "consistency":
// Often used on MACs and mistakenly on Linux platforms.
// Since Docker ignores this option so shall we.
continue
case "idmap":
if len(kv) > 1 {
mnt.Options = append(mnt.Options, fmt.Sprintf("idmap=%s", kv[1]))
if hasValue {
mnt.Options = append(mnt.Options, fmt.Sprintf("idmap=%s", value))
} else {
mnt.Options = append(mnt.Options, "idmap")
}
@ -294,42 +294,41 @@ func parseMountOptions(mountType string, args []string) (*spec.Mount, error) {
// ro=[true|false]
// rw
// rw=[true|false]
if kv[0] == "readonly" {
kv[0] = "ro"
if name == "readonly" {
name = "ro"
}
switch len(kv) {
case 1:
mnt.Options = append(mnt.Options, kv[0])
case 2:
switch strings.ToLower(kv[1]) {
if hasValue {
switch strings.ToLower(value) {
case "true":
mnt.Options = append(mnt.Options, kv[0])
mnt.Options = append(mnt.Options, name)
case "false":
// Set the opposite only for rw
// ro's opposite is the default
if kv[0] == "rw" {
if name == "rw" {
mnt.Options = append(mnt.Options, "ro")
}
}
} else {
mnt.Options = append(mnt.Options, name)
}
case "nodev", "dev":
if setDev {
return nil, fmt.Errorf("cannot pass 'nodev' and 'dev' mnt.Options more than once: %w", errOptionArg)
}
setDev = true
mnt.Options = append(mnt.Options, kv[0])
mnt.Options = append(mnt.Options, name)
case "noexec", "exec":
if setExec {
return nil, fmt.Errorf("cannot pass 'noexec' and 'exec' mnt.Options more than once: %w", errOptionArg)
}
setExec = true
mnt.Options = append(mnt.Options, kv[0])
mnt.Options = append(mnt.Options, name)
case "nosuid", "suid":
if setSuid {
return nil, fmt.Errorf("cannot pass 'nosuid' and 'suid' mnt.Options more than once: %w", errOptionArg)
}
setSuid = true
mnt.Options = append(mnt.Options, kv[0])
mnt.Options = append(mnt.Options, name)
case "noswap":
if setSwap {
return nil, fmt.Errorf("cannot pass 'noswap' mnt.Options more than once: %w", errOptionArg)
@ -338,80 +337,80 @@ func parseMountOptions(mountType string, args []string) (*spec.Mount, error) {
return nil, fmt.Errorf("the 'noswap' option is only allowed with rootful tmpfs mounts: %w", errOptionArg)
}
setSwap = true
mnt.Options = append(mnt.Options, kv[0])
mnt.Options = append(mnt.Options, name)
case "relabel":
if setRelabel {
return nil, fmt.Errorf("cannot pass 'relabel' option more than once: %w", errOptionArg)
}
setRelabel = true
if len(kv) != 2 {
return nil, fmt.Errorf("%s mount option must be 'private' or 'shared': %w", kv[0], util.ErrBadMntOption)
if !hasValue {
return nil, fmt.Errorf("%s mount option must be 'private' or 'shared': %w", name, util.ErrBadMntOption)
}
switch kv[1] {
switch value {
case "private":
mnt.Options = append(mnt.Options, "Z")
case "shared":
mnt.Options = append(mnt.Options, "z")
default:
return nil, fmt.Errorf("%s mount option must be 'private' or 'shared': %w", kv[0], util.ErrBadMntOption)
return nil, fmt.Errorf("%s mount option must be 'private' or 'shared': %w", name, util.ErrBadMntOption)
}
case "shared", "rshared", "private", "rprivate", "slave", "rslave", "unbindable", "runbindable", "Z", "z", "no-dereference":
mnt.Options = append(mnt.Options, kv[0])
mnt.Options = append(mnt.Options, name)
case "src", "source":
if mountType == define.TypeTmpfs {
return nil, fmt.Errorf("%q option not supported for %q mount types", kv[0], mountType)
return nil, fmt.Errorf("%q option not supported for %q mount types", name, mountType)
}
if mnt.Source != "" {
return nil, fmt.Errorf("cannot pass %q option more than once: %w", kv[0], errOptionArg)
return nil, fmt.Errorf("cannot pass %q option more than once: %w", name, errOptionArg)
}
if len(kv) == 1 {
return nil, fmt.Errorf("%v: %w", kv[0], errOptionArg)
if !hasValue {
return nil, fmt.Errorf("%v: %w", name, errOptionArg)
}
if len(kv[1]) == 0 {
if len(value) == 0 {
return nil, fmt.Errorf("host directory cannot be empty: %w", errOptionArg)
}
mnt.Source = kv[1]
mnt.Source = value
case "target", "dst", "destination":
if mnt.Destination != "" {
return nil, fmt.Errorf("cannot pass %q option more than once: %w", kv[0], errOptionArg)
return nil, fmt.Errorf("cannot pass %q option more than once: %w", name, errOptionArg)
}
if len(kv) == 1 {
return nil, fmt.Errorf("%v: %w", kv[0], errOptionArg)
if !hasValue {
return nil, fmt.Errorf("%v: %w", name, errOptionArg)
}
if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
if err := parse.ValidateVolumeCtrDir(value); err != nil {
return nil, err
}
mnt.Destination = unixPathClean(kv[1])
mnt.Destination = unixPathClean(value)
case "tmpcopyup", "notmpcopyup":
if mountType != define.TypeTmpfs {
return nil, fmt.Errorf("%q option not supported for %q mount types", kv[0], mountType)
return nil, fmt.Errorf("%q option not supported for %q mount types", name, mountType)
}
if setTmpcopyup {
return nil, fmt.Errorf("cannot pass 'tmpcopyup' and 'notmpcopyup' mnt.Options more than once: %w", errOptionArg)
}
setTmpcopyup = true
mnt.Options = append(mnt.Options, kv[0])
mnt.Options = append(mnt.Options, name)
case "tmpfs-mode":
if mountType != define.TypeTmpfs {
return nil, fmt.Errorf("%q option not supported for %q mount types", kv[0], mountType)
return nil, fmt.Errorf("%q option not supported for %q mount types", name, mountType)
}
if len(kv) == 1 {
return nil, fmt.Errorf("%v: %w", kv[0], errOptionArg)
if !hasValue {
return nil, fmt.Errorf("%v: %w", name, errOptionArg)
}
mnt.Options = append(mnt.Options, fmt.Sprintf("mode=%s", kv[1]))
mnt.Options = append(mnt.Options, fmt.Sprintf("mode=%s", value))
case "tmpfs-size":
if mountType != define.TypeTmpfs {
return nil, fmt.Errorf("%q option not supported for %q mount types", kv[0], mountType)
return nil, fmt.Errorf("%q option not supported for %q mount types", name, mountType)
}
if len(kv) == 1 {
return nil, fmt.Errorf("%v: %w", kv[0], errOptionArg)
if !hasValue {
return nil, fmt.Errorf("%v: %w", name, errOptionArg)
}
mnt.Options = append(mnt.Options, fmt.Sprintf("size=%s", kv[1]))
mnt.Options = append(mnt.Options, fmt.Sprintf("size=%s", value))
case "U", "chown":
if setOwnership {
return nil, fmt.Errorf("cannot pass 'U' or 'chown' option more than once: %w", errOptionArg)
}
ok, err := validChownFlag(val)
ok, err := validChownFlag(value)
if err != nil {
return nil, err
}
@ -421,16 +420,16 @@ func parseMountOptions(mountType string, args []string) (*spec.Mount, error) {
setOwnership = true
case "volume-label":
if mountType != define.TypeVolume {
return nil, fmt.Errorf("%q option not supported for %q mount types", kv[0], mountType)
return nil, fmt.Errorf("%q option not supported for %q mount types", name, mountType)
}
return nil, fmt.Errorf("the --volume-label option is not presently implemented")
case "volume-opt":
if mountType != define.TypeVolume {
return nil, fmt.Errorf("%q option not supported for %q mount types", kv[0], mountType)
return nil, fmt.Errorf("%q option not supported for %q mount types", name, mountType)
}
mnt.Options = append(mnt.Options, val)
mnt.Options = append(mnt.Options, arg)
default:
return nil, fmt.Errorf("%s: %w", kv[0], util.ErrBadMntOption)
return nil, fmt.Errorf("%s: %w", name, util.ErrBadMntOption)
}
}
if mountType != "glob" && len(mnt.Destination) == 0 {
@ -534,22 +533,22 @@ func getDevptsMount(args []string) (spec.Mount, error) {
var setDest bool
for _, val := range args {
kv := strings.SplitN(val, "=", 2)
switch kv[0] {
for _, arg := range args {
name, value, hasValue := strings.Cut(arg, "=")
switch name {
case "uid", "gid", "mode", "ptxmode", "newinstance", "max":
newMount.Options = append(newMount.Options, val)
newMount.Options = append(newMount.Options, arg)
case "target", "dst", "destination":
if len(kv) == 1 {
return newMount, fmt.Errorf("%v: %w", kv[0], errOptionArg)
if !hasValue {
return newMount, fmt.Errorf("%v: %w", name, errOptionArg)
}
if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
if err := parse.ValidateVolumeCtrDir(value); err != nil {
return newMount, err
}
newMount.Destination = unixPathClean(kv[1])
newMount.Destination = unixPathClean(value)
setDest = true
default:
return newMount, fmt.Errorf("%s: %w", kv[0], util.ErrBadMntOption)
return newMount, fmt.Errorf("%s: %w", name, util.ErrBadMntOption)
}
}
@ -586,37 +585,37 @@ func getNamedVolume(args []string) (*specgen.NamedVolume, error) {
func getImageVolume(args []string) (*specgen.ImageVolume, error) {
newVolume := new(specgen.ImageVolume)
for _, val := range args {
kv := strings.SplitN(val, "=", 2)
switch kv[0] {
for _, arg := range args {
name, value, hasValue := strings.Cut(arg, "=")
switch name {
case "src", "source":
if len(kv) == 1 {
return nil, fmt.Errorf("%v: %w", kv[0], errOptionArg)
if !hasValue {
return nil, fmt.Errorf("%v: %w", name, errOptionArg)
}
newVolume.Source = kv[1]
newVolume.Source = value
case "target", "dst", "destination":
if len(kv) == 1 {
return nil, fmt.Errorf("%v: %w", kv[0], errOptionArg)
if !hasValue {
return nil, fmt.Errorf("%v: %w", name, errOptionArg)
}
if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
if err := parse.ValidateVolumeCtrDir(value); err != nil {
return nil, err
}
newVolume.Destination = unixPathClean(kv[1])
newVolume.Destination = unixPathClean(value)
case "rw", "readwrite":
switch kv[1] {
switch value {
case "true":
newVolume.ReadWrite = true
case "false":
// Nothing to do. RO is default.
default:
return nil, fmt.Errorf("invalid rw value %q: %w", kv[1], util.ErrBadMntOption)
return nil, fmt.Errorf("invalid rw value %q: %w", value, util.ErrBadMntOption)
}
case "consistency":
// Often used on MACs and mistakenly on Linux platforms.
// Since Docker ignores this option so shall we.
continue
default:
return nil, fmt.Errorf("%s: %w", kv[0], util.ErrBadMntOption)
return nil, fmt.Errorf("%s: %w", name, util.ErrBadMntOption)
}
}
@ -660,24 +659,16 @@ func getTmpfsMounts(tmpfsFlag []string) (map[string]spec.Mount, error) {
}
// validChownFlag ensures that the U or chown flag is correctly used
func validChownFlag(flag string) (bool, error) {
kv := strings.SplitN(flag, "=", 2)
switch len(kv) {
case 1:
case 2:
// U=[true|false]
switch strings.ToLower(kv[1]) {
case "true":
case "false":
return false, nil
default:
return false, fmt.Errorf("'U' or 'chown' must be set to true or false, instead received %q: %w", kv[1], errOptionArg)
}
func validChownFlag(value string) (bool, error) {
// U=[true|false]
switch {
case strings.EqualFold(value, "true"), value == "":
return true, nil
case strings.EqualFold(value, "false"):
return false, nil
default:
return false, fmt.Errorf("badly formatted option %q: %w", flag, errOptionArg)
return false, fmt.Errorf("'U' or 'chown' must be set to true or false, instead received %q: %w", value, errOptionArg)
}
return true, nil
}
// Use path instead of filepath to preserve Unix style paths on Windows

View file

@ -13,41 +13,41 @@ func Test_validChownFlag(t *testing.T) {
wantErr bool
}{
{
name: "U true",
name: "lower-case true",
args: args{
flag: "U=true",
flag: "true",
},
want: true,
wantErr: false,
},
{
name: "U true case does not matter",
name: "case-insensitive true",
args: args{
flag: "u=True",
flag: "True",
},
want: true,
wantErr: false,
},
{
name: "U is false",
name: "lower-case false",
args: args{
flag: "U=false",
flag: "false",
},
want: false,
wantErr: false,
},
{
name: "chown should also work",
name: "case-insensitive false",
args: args{
flag: "chown=true",
flag: "falsE",
},
want: true,
want: false,
wantErr: false,
},
{
name: "garbage value should fail",
args: args{
flag: "U=foobar",
flag: "foobar",
},
want: false,
wantErr: true,

View file

@ -471,8 +471,8 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst
// because it does try to red the value from the environment
if !strings.Contains(env, "=") {
for _, containerEnv := range info.containerEnv {
split := strings.SplitN(containerEnv, "=", 2)
if split[0] == env {
key, _, _ := strings.Cut(containerEnv, "=")
if key == env {
info.ExtraEnvs = append(info.ExtraEnvs, escapeSystemdArg(containerEnv))
}
}

View file

@ -1118,10 +1118,10 @@ func ConvertKube(kube *parser.UnitFile, names map[string]string, isUser bool) (*
for _, update := range updateMaps {
annotation := fmt.Sprintf("--annotation=%s", autoUpdateLabel)
updateType := update
val := strings.SplitN(update, "/", 2)
if len(val) == 2 {
annotation = annotation + "/" + val[0]
updateType = val[1]
annoValue, typ, hasSlash := strings.Cut(update, "/")
if hasSlash {
annotation = annotation + "/" + annoValue
updateType = typ
}
execStart.addf("%s=%s", annotation, updateType)
}
@ -1741,13 +1741,13 @@ func resolveContainerMountParams(containerUnitFile, serviceUnitFile *parser.Unit
sourceIndex := -1
originalSource := ""
for i, token := range tokens {
kv := strings.SplitN(token, "=", 2)
if kv[0] == "source" || kv[0] == "src" {
if len(kv) < 2 {
key, val, hasVal := strings.Cut(token, "=")
if key == "source" || key == "src" {
if !hasVal {
return "", fmt.Errorf("source parameter does not include a value")
}
sourceIndex = i
originalSource = kv[1]
originalSource = val
}
}

View file

@ -116,19 +116,19 @@ func ParseTimestamps(value string, def int64) (int64, int64, error) {
}
func parseTimestamp(value string) (int64, int64, error) {
sa := strings.SplitN(value, ".", 2)
s, err := strconv.ParseInt(sa[0], 10, 64)
spart, npart, hasParts := strings.Cut(value, ".")
s, err := strconv.ParseInt(spart, 10, 64)
if err != nil {
return s, 0, err
}
if len(sa) != 2 {
if !hasParts {
return s, 0, nil
}
n, err := strconv.ParseInt(sa[1], 10, 64)
n, err := strconv.ParseInt(npart, 10, 64)
if err != nil {
return s, n, err
}
// should already be in nanoseconds but just in case convert n to nanoseconds
n = int64(float64(n) * math.Pow(float64(10), float64(9-len(sa[1]))))
n = int64(float64(n) * math.Pow(float64(10), float64(9-len(npart))))
return s, n, nil
}

View file

@ -131,8 +131,11 @@ func parseUids(colonDelimitKeys []byte) []string {
continue
}
parseduid := uid
if strings.Contains(uid, "<") && strings.Contains(uid, ">") {
parseduid = strings.SplitN(strings.SplitAfterN(uid, "<", 2)[1], ">", 2)[0]
if ltidx := strings.Index(uid, "<"); ltidx != -1 {
subuid := parseduid[ltidx+1:]
if gtidx := strings.Index(subuid, ">"); gtidx != -1 {
parseduid = subuid[:gtidx]
}
}
parseduids = append(parseduids, parseduid)
}

View file

@ -65,9 +65,9 @@ func PrepareFilters(r *http.Request) (*map[string][]string, error) {
}
filterMap := map[string][]string{}
for _, filter := range filtersList {
split := strings.SplitN(filter, "=", 2)
if len(split) > 1 {
filterMap[split[0]] = append(filterMap[split[0]], split[1])
fname, filter, hasFilter := strings.Cut(filter, "=")
if hasFilter {
filterMap[fname] = append(filterMap[fname], filter)
}
}
return &filterMap, nil

View file

@ -34,7 +34,7 @@ func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string
newOptions := make([]string, 0, len(options))
for _, opt := range options {
// Some options have parameters - size, mode
splitOpt := strings.SplitN(opt, "=", 2)
key, _, _ := strings.Cut(opt, "=")
// add advanced options such as upperdir=/path and workdir=/path, when overlay is specified
if foundOverlay {
@ -47,11 +47,11 @@ func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string
continue
}
}
if strings.HasPrefix(splitOpt[0], "subpath") {
if strings.HasPrefix(key, "subpath") {
newOptions = append(newOptions, opt)
continue
}
if strings.HasPrefix(splitOpt[0], "idmap") {
if strings.HasPrefix(key, "idmap") {
if foundIdmap {
return nil, fmt.Errorf("the 'idmap' option can only be set once: %w", ErrDupeMntOption)
}
@ -60,7 +60,7 @@ func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string
continue
}
switch splitOpt[0] {
switch key {
case "copy", "nocopy":
if foundCopy {
return nil, fmt.Errorf("only one of 'nocopy' and 'copy' can be used: %w", ErrDupeMntOption)
@ -210,13 +210,13 @@ func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string
}
func ParseDriverOpts(option string) (string, string, error) {
token := strings.SplitN(option, "=", 2)
if len(token) != 2 {
_, val, hasVal := strings.Cut(option, "=")
if !hasVal {
return "", "", fmt.Errorf("cannot parse driver opts: %w", ErrBadMntOption)
}
opt := strings.SplitN(token[1], "=", 2)
if len(opt) != 2 {
optKey, optVal, hasOptVal := strings.Cut(val, "=")
if !hasOptVal {
return "", "", fmt.Errorf("cannot parse driver opts: %w", ErrBadMntOption)
}
return opt[0], opt[1], nil
return optKey, optVal, nil
}

View file

@ -55,14 +55,8 @@ func init() {
// Helper function to determine the username/password passed
// in the creds string. It could be either or both.
func parseCreds(creds string) (string, string) {
if creds == "" {
return "", ""
}
up := strings.SplitN(creds, ":", 2)
if len(up) == 1 {
return up[0], ""
}
return up[0], up[1]
username, password, _ := strings.Cut(creds, ":")
return username, password
}
// Takes build context and validates `.containerignore` or `.dockerignore`
@ -918,12 +912,12 @@ func parseAutoIDMap(mapSpec string, mapSetting string, parentMapping []ruser.IDM
// GetAutoOptions returns an AutoUserNsOptions with the settings to automatically set up
// a user namespace.
func GetAutoOptions(n namespaces.UsernsMode) (*stypes.AutoUserNsOptions, error) {
parts := strings.SplitN(string(n), ":", 2)
if parts[0] != "auto" {
mode, opts, hasOpts := strings.Cut(string(n), ":")
if mode != "auto" {
return nil, fmt.Errorf("wrong user namespace mode")
}
options := stypes.AutoUserNsOptions{}
if len(parts) == 1 {
if !hasOpts {
return &options, nil
}
@ -937,32 +931,32 @@ func GetAutoOptions(n namespaces.UsernsMode) (*stypes.AutoUserNsOptions, error)
}
}
for _, o := range strings.Split(parts[1], ",") {
v := strings.SplitN(o, "=", 2)
if len(v) != 2 {
for _, o := range strings.Split(opts, ",") {
key, val, hasVal := strings.Cut(o, "=")
if !hasVal {
return nil, fmt.Errorf("invalid option specified: %q", o)
}
switch v[0] {
switch key {
case "size":
s, err := strconv.ParseUint(v[1], 10, 32)
s, err := strconv.ParseUint(val, 10, 32)
if err != nil {
return nil, err
}
options.Size = uint32(s)
case "uidmapping":
mapping, err := parseAutoIDMap(v[1], "UID", parentUIDMap)
mapping, err := parseAutoIDMap(val, "UID", parentUIDMap)
if err != nil {
return nil, err
}
options.AdditionalUIDMappings = append(options.AdditionalUIDMappings, mapping...)
case "gidmapping":
mapping, err := parseAutoIDMap(v[1], "GID", parentGIDMap)
mapping, err := parseAutoIDMap(val, "GID", parentGIDMap)
if err != nil {
return nil, err
}
options.AdditionalGIDMappings = append(options.AdditionalGIDMappings, mapping...)
default:
return nil, fmt.Errorf("unknown option specified: %q", v[0])
return nil, fmt.Errorf("unknown option specified: %q", key)
}
}
return &options, nil
@ -1077,9 +1071,9 @@ func getTomlStorage(storeOptions *stypes.StoreOptions) *tomlConfig {
config.Storage.RunRoot = storeOptions.RunRoot
config.Storage.GraphRoot = storeOptions.GraphRoot
for _, i := range storeOptions.GraphDriverOptions {
s := strings.SplitN(i, "=", 2)
if s[0] == "overlay.mount_program" && len(s) == 2 {
config.Storage.Options.MountProgram = s[1]
program, hasPrefix := strings.CutPrefix(i, "overlay.mount_program=")
if hasPrefix {
config.Storage.Options.MountProgram = program
}
}

View file

@ -209,12 +209,11 @@ var _ = SynchronizedAfterSuite(func() {
scanner := bufio.NewScanner(f)
for scanner.Scan() {
text := scanner.Text()
timing := strings.SplitN(text, "\t\t", 2)
if len(timing) != 2 {
name, durationString, ok := strings.Cut(text, "\t\t")
if !ok {
Fail(fmt.Sprintf("incorrect timing line: %q", text))
}
name := timing[0]
duration, err := strconv.ParseFloat(timing[1], 64)
duration, err := strconv.ParseFloat(durationString, 64)
Expect(err).ToNot(HaveOccurred(), "failed to parse float from timings file")
testTimings = append(testTimings, testResult{name: name, length: duration})
}

View file

@ -202,12 +202,8 @@ func keyValueStringToMap(keyValueString, separator string) (map[string]string, e
return nil, err
}
for _, param := range keyVarList[0] {
val := ""
kv := strings.SplitN(param, "=", 2)
if len(kv) == 2 {
val = kv[1]
}
keyValMap[kv[0]] = val
key, val, _ := strings.Cut(param, "=")
keyValMap[key] = val
}
return keyValMap, nil