mirror of
https://github.com/containers/podman.git
synced 2025-05-17 15:18:43 +08:00
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:
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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])
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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:], ","))
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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).
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
32
pkg/env/env.go
vendored
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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})
|
||||
}
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user