mirror of https://github.com/containers/podman.git
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:
parent
f1ea4fbb3d
commit
522934d5cf
|
|
@ -333,15 +333,15 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *Buil
|
||||||
}
|
}
|
||||||
if c.Flag("build-arg").Changed {
|
if c.Flag("build-arg").Changed {
|
||||||
for _, arg := range flags.BuildArg {
|
for _, arg := range flags.BuildArg {
|
||||||
av := strings.SplitN(arg, "=", 2)
|
key, val, hasVal := strings.Cut(arg, "=")
|
||||||
if len(av) > 1 {
|
if hasVal {
|
||||||
args[av[0]] = av[1]
|
args[key] = val
|
||||||
} else {
|
} else {
|
||||||
// check if the env is set in the local environment and use that value if it is
|
// 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 {
|
if val, present := os.LookupEnv(key); present {
|
||||||
args[av[0]] = val
|
args[key] = val
|
||||||
} else {
|
} 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)
|
additionalBuildContext := make(map[string]*buildahDefine.AdditionalBuildContext)
|
||||||
if c.Flag("build-context").Changed {
|
if c.Flag("build-context").Changed {
|
||||||
for _, contextString := range flags.BuildContext {
|
for _, contextString := range flags.BuildContext {
|
||||||
av := strings.SplitN(contextString, "=", 2)
|
key, val, hasVal := strings.Cut(contextString, "=")
|
||||||
if len(av) > 1 {
|
if hasVal {
|
||||||
parseAdditionalBuildContext, err := parse.GetAdditionalBuildContext(av[1])
|
parseAdditionalBuildContext, err := parse.GetAdditionalBuildContext(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("while parsing additional build context: %w", err)
|
return nil, fmt.Errorf("while parsing additional build context: %w", err)
|
||||||
}
|
}
|
||||||
additionalBuildContext[av[0]] = &parseAdditionalBuildContext
|
additionalBuildContext[key] = &parseAdditionalBuildContext
|
||||||
} else {
|
} 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 {
|
func suffixCompSlice(suf string, slice []string) []string {
|
||||||
for i := range slice {
|
for i := range slice {
|
||||||
split := strings.SplitN(slice[i], "\t", 2)
|
key, val, hasVal := strings.Cut(slice[i], "\t")
|
||||||
if len(split) > 1 {
|
if hasVal {
|
||||||
slice[i] = split[0] + suf + "\t" + split[1]
|
slice[i] = key + suf + "\t" + val
|
||||||
} else {
|
} else {
|
||||||
slice[i] += suf
|
slice[i] += suf
|
||||||
}
|
}
|
||||||
|
|
@ -878,10 +878,10 @@ func AutocompleteScp(cmd *cobra.Command, args []string, toComplete string) ([]st
|
||||||
}
|
}
|
||||||
switch len(args) {
|
switch len(args) {
|
||||||
case 0:
|
case 0:
|
||||||
split := strings.SplitN(toComplete, "::", 2)
|
prefix, imagesToComplete, isImages := strings.Cut(toComplete, "::")
|
||||||
if len(split) > 1 {
|
if isImages {
|
||||||
imageSuggestions, _ := getImages(cmd, split[1])
|
imageSuggestions, _ := getImages(cmd, imagesToComplete)
|
||||||
return prefixSlice(split[0]+"::", imageSuggestions), cobra.ShellCompDirectiveNoFileComp
|
return prefixSlice(prefix+"::", imageSuggestions), cobra.ShellCompDirectiveNoFileComp
|
||||||
}
|
}
|
||||||
connectionSuggestions, _ := AutocompleteSystemConnections(cmd, args, toComplete)
|
connectionSuggestions, _ := AutocompleteSystemConnections(cmd, args, toComplete)
|
||||||
imageSuggestions, _ := getImages(cmd, toComplete)
|
imageSuggestions, _ := getImages(cmd, toComplete)
|
||||||
|
|
@ -893,9 +893,9 @@ func AutocompleteScp(cmd *cobra.Command, args []string, toComplete string) ([]st
|
||||||
}
|
}
|
||||||
return totalSuggestions, directive
|
return totalSuggestions, directive
|
||||||
case 1:
|
case 1:
|
||||||
split := strings.SplitN(args[0], "::", 2)
|
_, imagesToComplete, isImages := strings.Cut(args[0], "::")
|
||||||
if len(split) > 1 {
|
if isImages {
|
||||||
if len(split[1]) > 0 {
|
if len(imagesToComplete) > 0 {
|
||||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||||
}
|
}
|
||||||
imageSuggestions, _ := getImages(cmd, toComplete)
|
imageSuggestions, _ := getImages(cmd, toComplete)
|
||||||
|
|
@ -1076,7 +1076,7 @@ func AutocompleteUserFlag(cmd *cobra.Command, args []string, toComplete string)
|
||||||
|
|
||||||
var groups []string
|
var groups []string
|
||||||
scanner := bufio.NewScanner(file)
|
scanner := bufio.NewScanner(file)
|
||||||
user := strings.SplitN(toComplete, ":", 2)[0]
|
user, _, _ := strings.Cut(toComplete, ":")
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
entries := strings.SplitN(scanner.Text(), ":", 4)
|
entries := strings.SplitN(scanner.Text(), ":", 4)
|
||||||
groups = append(groups, user+":"+entries[0])
|
groups = append(groups, user+":"+entries[0])
|
||||||
|
|
|
||||||
|
|
@ -342,10 +342,10 @@ func PullImage(imageName string, cliVals *entities.ContainerCreateOptions) (stri
|
||||||
if cliVals.Arch != "" || cliVals.OS != "" {
|
if cliVals.Arch != "" || cliVals.OS != "" {
|
||||||
return "", errors.New("--platform option can not be specified with --arch or --os")
|
return "", errors.New("--platform option can not be specified with --arch or --os")
|
||||||
}
|
}
|
||||||
split := strings.SplitN(cliVals.Platform, "/", 2)
|
OS, Arch, hasArch := strings.Cut(cliVals.Platform, "/")
|
||||||
cliVals.OS = split[0]
|
cliVals.OS = OS
|
||||||
if len(split) > 1 {
|
if hasArch {
|
||||||
cliVals.Arch = split[1]
|
cliVals.Arch = Arch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,11 +102,11 @@ func pause(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range filters {
|
for _, f := range filters {
|
||||||
split := strings.SplitN(f, "=", 2)
|
fname, filter, hasFilter := strings.Cut(f, "=")
|
||||||
if len(split) < 2 {
|
if !hasFilter {
|
||||||
return fmt.Errorf("invalid filter %q", f)
|
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)
|
responses, err := registry.ContainerEngine().ContainerPause(context.Background(), args, pauseOpts)
|
||||||
|
|
|
||||||
|
|
@ -195,11 +195,11 @@ func ps(cmd *cobra.Command, _ []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range filters {
|
for _, f := range filters {
|
||||||
split := strings.SplitN(f, "=", 2)
|
fname, filter, hasFilter := strings.Cut(f, "=")
|
||||||
if len(split) == 1 {
|
if !hasFilter {
|
||||||
return fmt.Errorf("invalid filter %q", f)
|
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()
|
listContainers, err := getResponses()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -114,11 +114,11 @@ func restart(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range filters {
|
for _, f := range filters {
|
||||||
split := strings.SplitN(f, "=", 2)
|
fname, filter, hasFilter := strings.Cut(f, "=")
|
||||||
if len(split) < 2 {
|
if !hasFilter {
|
||||||
return fmt.Errorf("invalid filter %q", f)
|
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)
|
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)
|
return fmt.Errorf("reading CIDFile: %w", err)
|
||||||
}
|
}
|
||||||
id := strings.Split(string(content), "\n")[0]
|
id, _, _ := strings.Cut(string(content), "\n")
|
||||||
args = append(args, id)
|
args = append(args, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range filters {
|
for _, f := range filters {
|
||||||
split := strings.SplitN(f, "=", 2)
|
fname, filter, hasFilter := strings.Cut(f, "=")
|
||||||
if len(split) < 2 {
|
if !hasFilter {
|
||||||
return fmt.Errorf("invalid filter %q", f)
|
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 {
|
if rmOptions.All {
|
||||||
|
|
|
||||||
|
|
@ -124,11 +124,11 @@ func start(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
containers := utils.RemoveSlash(args)
|
containers := utils.RemoveSlash(args)
|
||||||
for _, f := range filters {
|
for _, f := range filters {
|
||||||
split := strings.SplitN(f, "=", 2)
|
fname, filter, hasFilter := strings.Cut(f, "=")
|
||||||
if len(split) < 2 {
|
if !hasFilter {
|
||||||
return fmt.Errorf("invalid filter %q", f)
|
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)
|
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 {
|
for _, f := range filters {
|
||||||
split := strings.SplitN(f, "=", 2)
|
fname, filter, hasFilter := strings.Cut(f, "=")
|
||||||
if len(split) < 2 {
|
if !hasFilter {
|
||||||
return fmt.Errorf("invalid filter %q", f)
|
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)
|
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 {
|
for _, f := range filters {
|
||||||
split := strings.SplitN(f, "=", 2)
|
fname, filter, hasFilter := strings.Cut(f, "=")
|
||||||
if len(split) < 2 {
|
if !hasFilter {
|
||||||
return fmt.Errorf("invalid filter %q", f)
|
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)
|
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 != "" {
|
if pullOptions.Arch != "" || pullOptions.OS != "" {
|
||||||
return errors.New("--platform option can not be specified with --arch or --os")
|
return errors.New("--platform option can not be specified with --arch or --os")
|
||||||
}
|
}
|
||||||
split := strings.SplitN(platform, "/", 2)
|
|
||||||
pullOptions.OS = split[0]
|
specs := strings.Split(platform, "/")
|
||||||
if len(split) > 1 {
|
pullOptions.OS = specs[0] // may be empty
|
||||||
pullOptions.Arch = split[1]
|
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 {
|
for _, annotation := range playOptions.annotations {
|
||||||
splitN := strings.SplitN(annotation, "=", 2)
|
key, val, hasVal := strings.Cut(annotation, "=")
|
||||||
if len(splitN) != 2 {
|
if !hasVal {
|
||||||
return fmt.Errorf("annotation %q must include an '=' sign", annotation)
|
return fmt.Errorf("annotation %q must include an '=' sign", annotation)
|
||||||
}
|
}
|
||||||
if playOptions.Annotations == nil {
|
if playOptions.Annotations == nil {
|
||||||
playOptions.Annotations = make(map[string]string)
|
playOptions.Annotations = make(map[string]string)
|
||||||
}
|
}
|
||||||
annotation := splitN[1]
|
if len(val) > define.MaxKubeAnnotation && !playOptions.UseLongAnnotations {
|
||||||
if len(annotation) > define.MaxKubeAnnotation && !playOptions.UseLongAnnotations {
|
return fmt.Errorf("annotation exceeds maximum size, %d, of kubernetes annotation: %s", define.MaxKubeAnnotation, val)
|
||||||
return fmt.Errorf("annotation exceeds maximum size, %d, of kubernetes annotation: %s", define.MaxKubeAnnotation, annotation)
|
|
||||||
}
|
}
|
||||||
playOptions.Annotations[splitN[0]] = annotation
|
playOptions.Annotations[key] = val
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, mac := range playOptions.macs {
|
for _, mac := range playOptions.macs {
|
||||||
|
|
|
||||||
|
|
@ -245,16 +245,16 @@ func parseRoute(routeStr string) (*types.Route, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseRange(iprange string) (*types.LeaseRange, error) {
|
func parseRange(iprange string) (*types.LeaseRange, error) {
|
||||||
split := strings.SplitN(iprange, "-", 2)
|
startIPString, endIPString, hasDash := strings.Cut(iprange, "-")
|
||||||
if len(split) > 1 {
|
if hasDash {
|
||||||
// range contains dash so assume form is start-end
|
// range contains dash so assume form is start-end
|
||||||
start := net.ParseIP(split[0])
|
start := net.ParseIP(startIPString)
|
||||||
if start == nil {
|
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 {
|
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{
|
return &types.LeaseRange{
|
||||||
StartIP: start,
|
StartIP: start,
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,11 @@ import (
|
||||||
func FilterArgumentsIntoFilters(filters []string) (url.Values, error) {
|
func FilterArgumentsIntoFilters(filters []string) (url.Values, error) {
|
||||||
parsedFilters := make(url.Values)
|
parsedFilters := make(url.Values)
|
||||||
for _, f := range filters {
|
for _, f := range filters {
|
||||||
t := strings.SplitN(f, "=", 2)
|
fname, filter, hasFilter := strings.Cut(f, "=")
|
||||||
if len(t) < 2 {
|
if !hasFilter {
|
||||||
return parsedFilters, fmt.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
|
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
|
return parsedFilters, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,15 +33,15 @@ var (
|
||||||
// for add-host flag
|
// for add-host flag
|
||||||
func ValidateExtraHost(val string) (string, error) {
|
func ValidateExtraHost(val string) (string, error) {
|
||||||
// allow for IPv6 addresses in extra hosts by only splitting on first ":"
|
// allow for IPv6 addresses in extra hosts by only splitting on first ":"
|
||||||
arr := strings.SplitN(val, ":", 2)
|
name, ip, hasIP := strings.Cut(val, ":")
|
||||||
if len(arr) != 2 || len(arr[0]) == 0 {
|
if !hasIP || len(name) == 0 {
|
||||||
return "", fmt.Errorf("bad format for add-host: %q", val)
|
return "", fmt.Errorf("bad format for add-host: %q", val)
|
||||||
}
|
}
|
||||||
if arr[1] == etchosts.HostGateway {
|
if ip == etchosts.HostGateway {
|
||||||
return val, nil
|
return val, nil
|
||||||
}
|
}
|
||||||
if _, err := validateIPAddress(arr[1]); err != nil {
|
if _, err := validateIPAddress(ip); err != nil {
|
||||||
return "", fmt.Errorf("invalid IP address in add-host: %q", arr[1])
|
return "", fmt.Errorf("invalid IP address in add-host: %q", ip)
|
||||||
}
|
}
|
||||||
return val, nil
|
return val, nil
|
||||||
}
|
}
|
||||||
|
|
@ -82,45 +82,37 @@ func GetAllLabels(labelFile, inputLabels []string) (map[string]string, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, label := range inputLabels {
|
for _, label := range inputLabels {
|
||||||
split := strings.SplitN(label, "=", 2)
|
key, value, _ := strings.Cut(label, "=")
|
||||||
if split[0] == "" {
|
if key == "" {
|
||||||
return nil, fmt.Errorf("invalid label format: %q", label)
|
return nil, fmt.Errorf("invalid label format: %q", label)
|
||||||
}
|
}
|
||||||
value := ""
|
labels[key] = value
|
||||||
if len(split) > 1 {
|
|
||||||
value = split[1]
|
|
||||||
}
|
|
||||||
labels[split[0]] = value
|
|
||||||
}
|
}
|
||||||
return labels, nil
|
return labels, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseEnvOrLabel(env map[string]string, line, configType string) error {
|
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"
|
// catch invalid variables such as "=" or "=A"
|
||||||
if data[0] == "" {
|
if key == "" {
|
||||||
return fmt.Errorf("invalid environment variable: %q", line)
|
return fmt.Errorf("invalid environment variable: %q", line)
|
||||||
}
|
}
|
||||||
|
|
||||||
// trim the front of a variable, but nothing else
|
// 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) {
|
if strings.ContainsAny(name, whiteSpaces) {
|
||||||
return fmt.Errorf("name %q has white spaces, poorly formatted name", name)
|
return fmt.Errorf("name %q has white spaces, poorly formatted name", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(data) > 1 {
|
if hasVal {
|
||||||
env[name] = data[1]
|
env[name] = val
|
||||||
} else {
|
} else {
|
||||||
if strings.HasSuffix(name, "*") {
|
if name, hasStar := strings.CutSuffix(name, "*"); hasStar {
|
||||||
name = strings.TrimSuffix(name, "*")
|
|
||||||
for _, e := range os.Environ() {
|
for _, e := range os.Environ() {
|
||||||
part := strings.SplitN(e, "=", 2)
|
envKey, envVal, hasEq := strings.Cut(e, "=")
|
||||||
if len(part) < 2 {
|
if hasEq && strings.HasPrefix(envKey, name) {
|
||||||
continue
|
env[envKey] = envVal
|
||||||
}
|
|
||||||
if strings.HasPrefix(part[0], name) {
|
|
||||||
env[part[0]] = part[1]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if configType == ENVType {
|
} else if configType == ENVType {
|
||||||
|
|
|
||||||
|
|
@ -81,11 +81,11 @@ func pods(cmd *cobra.Command, _ []string) error {
|
||||||
if cmd.Flag("filter").Changed {
|
if cmd.Flag("filter").Changed {
|
||||||
psInput.Filters = make(map[string][]string)
|
psInput.Filters = make(map[string][]string)
|
||||||
for _, f := range inputFilters {
|
for _, f := range inputFilters {
|
||||||
split := strings.SplitN(f, "=", 2)
|
fname, filter, hasFilter := strings.Cut(f, "=")
|
||||||
if len(split) < 2 {
|
if !hasFilter {
|
||||||
return fmt.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
|
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)
|
responses, err := registry.ContainerEngine().PodPs(context.Background(), psInput)
|
||||||
|
|
|
||||||
|
|
@ -261,15 +261,15 @@ func translateDest(path string) (string, error) {
|
||||||
if path == "" {
|
if path == "" {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
split := strings.SplitN(path, "=", 2)
|
key, val, hasVal := strings.Cut(path, "=")
|
||||||
if len(split) == 1 {
|
if !hasVal {
|
||||||
return split[0], nil
|
return key, nil
|
||||||
}
|
}
|
||||||
if split[0] != "host" {
|
if key != "host" {
|
||||||
return "", fmt.Errorf("\"host\" is requited for --docker option")
|
return "", fmt.Errorf("\"host\" is required for --docker option")
|
||||||
}
|
}
|
||||||
// "host=tcp://myserver:2376,ca=~/ca-file,cert=~/cert-file,key=~/key-file"
|
// "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 {
|
if len(vals) > 1 {
|
||||||
return "", fmt.Errorf("--docker additional options %q not supported", strings.Join(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?
|
// Should we store the ENV we actually want in the spec separately?
|
||||||
if c.config.Spec.Process != nil {
|
if c.config.Spec.Process != nil {
|
||||||
for _, e := range c.config.Spec.Process.Env {
|
for _, e := range c.config.Spec.Process.Env {
|
||||||
splitEnv := strings.SplitN(e, "=", 2)
|
key, val, _ := strings.Cut(e, "=")
|
||||||
importBuilder.SetEnv(splitEnv[0], splitEnv[1])
|
importBuilder.SetEnv(key, val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Expose ports
|
// Expose ports
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,7 @@ func (c *Container) addSharedNamespaces(g *generate.Generator) error {
|
||||||
}
|
}
|
||||||
needEnv := true
|
needEnv := true
|
||||||
for _, checkEnv := range g.Config.Process.Env {
|
for _, checkEnv := range g.Config.Process.Env {
|
||||||
if strings.SplitN(checkEnv, "=", 2)[0] == "HOSTNAME" {
|
if strings.HasPrefix(checkEnv, "HOSTNAME=") {
|
||||||
needEnv = false
|
needEnv = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,13 +73,13 @@ func (p *Pod) GenerateForKube(ctx context.Context, getService, useLongAnnotation
|
||||||
return nil, servicePorts, err
|
return nil, servicePorts, err
|
||||||
}
|
}
|
||||||
for _, host := range infraContainer.config.ContainerNetworkConfig.HostAdd {
|
for _, host := range infraContainer.config.ContainerNetworkConfig.HostAdd {
|
||||||
hostSli := strings.SplitN(host, ":", 2)
|
hostname, ip, hasIP := strings.Cut(host, ":")
|
||||||
if len(hostSli) != 2 {
|
if !hasIP {
|
||||||
return nil, servicePorts, errors.New("invalid hostAdd")
|
return nil, servicePorts, errors.New("invalid hostAdd")
|
||||||
}
|
}
|
||||||
extraHost = append(extraHost, v1.HostAlias{
|
extraHost = append(extraHost, v1.HostAlias{
|
||||||
IP: hostSli[1],
|
IP: ip,
|
||||||
Hostnames: []string{hostSli[0]},
|
Hostnames: []string{hostname},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
ports, err = portMappingToContainerPort(infraContainer.config.PortMappings, getService)
|
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)
|
dnsOptions := make([]v1.PodDNSConfigOption, 0)
|
||||||
for _, option := range options {
|
for _, option := range options {
|
||||||
// the option can be "k:v" or just "k", no delimiter is required
|
// 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{
|
dnsOpt := v1.PodDNSConfigOption{
|
||||||
Name: opts[0],
|
Name: name,
|
||||||
Value: &opts[1],
|
Value: &value,
|
||||||
}
|
}
|
||||||
dnsOptions = append(dnsOptions, dnsOpt)
|
dnsOptions = append(dnsOptions, dnsOpt)
|
||||||
}
|
}
|
||||||
|
|
@ -1055,23 +1055,23 @@ func libpodEnvVarsToKubeEnvVars(envs []string, imageEnvs []string) ([]v1.EnvVar,
|
||||||
envVars := make([]v1.EnvVar, 0, len(envs))
|
envVars := make([]v1.EnvVar, 0, len(envs))
|
||||||
imageMap := make(map[string]string, len(imageEnvs))
|
imageMap := make(map[string]string, len(imageEnvs))
|
||||||
for _, ie := range imageEnvs {
|
for _, ie := range imageEnvs {
|
||||||
split := strings.SplitN(ie, "=", 2)
|
key, val, _ := strings.Cut(ie, "=")
|
||||||
imageMap[split[0]] = split[1]
|
imageMap[key] = val
|
||||||
}
|
}
|
||||||
for _, e := range envs {
|
for _, e := range envs {
|
||||||
split := strings.SplitN(e, "=", 2)
|
envName, envValue, hasValue := strings.Cut(e, "=")
|
||||||
if len(split) != 2 {
|
if !hasValue {
|
||||||
return envVars, fmt.Errorf("environment variable %s is malformed; should be key=value", e)
|
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
|
continue
|
||||||
}
|
}
|
||||||
if imageMap[split[0]] == split[1] {
|
if imageMap[envName] == envValue {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
ev := v1.EnvVar{
|
ev := v1.EnvVar{
|
||||||
Name: split[0],
|
Name: envName,
|
||||||
Value: split[1],
|
Value: envValue,
|
||||||
}
|
}
|
||||||
envVars = append(envVars, ev)
|
envVars = append(envVars, ev)
|
||||||
}
|
}
|
||||||
|
|
@ -1286,25 +1286,22 @@ func generateKubeSecurityContext(c *Container) (*v1.SecurityContext, bool, error
|
||||||
var selinuxOpts v1.SELinuxOptions
|
var selinuxOpts v1.SELinuxOptions
|
||||||
selinuxHasData := false
|
selinuxHasData := false
|
||||||
for _, label := range strings.Split(c.config.Spec.Annotations[define.InspectAnnotationLabel], ",label=") {
|
for _, label := range strings.Split(c.config.Spec.Annotations[define.InspectAnnotationLabel], ",label=") {
|
||||||
opts := strings.SplitN(label, ":", 2)
|
opt, val, hasVal := strings.Cut(label, ":")
|
||||||
switch len(opts) {
|
if hasVal {
|
||||||
case 2:
|
switch opt {
|
||||||
switch opts[0] {
|
|
||||||
case "filetype":
|
case "filetype":
|
||||||
selinuxOpts.FileType = opts[1]
|
selinuxOpts.FileType = val
|
||||||
selinuxHasData = true
|
selinuxHasData = true
|
||||||
case "type":
|
case "type":
|
||||||
selinuxOpts.Type = opts[1]
|
selinuxOpts.Type = val
|
||||||
selinuxHasData = true
|
selinuxHasData = true
|
||||||
case "level":
|
case "level":
|
||||||
selinuxOpts.Level = opts[1]
|
selinuxOpts.Level = val
|
||||||
selinuxHasData = true
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
if opts[0] == "disable" {
|
|
||||||
selinuxOpts.Type = "spc_t"
|
|
||||||
selinuxHasData = true
|
selinuxHasData = true
|
||||||
}
|
}
|
||||||
|
} else if opt == "disable" {
|
||||||
|
selinuxOpts.Type = "spc_t"
|
||||||
|
selinuxHasData = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if selinuxHasData {
|
if selinuxHasData {
|
||||||
|
|
|
||||||
|
|
@ -524,11 +524,11 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON,
|
||||||
|
|
||||||
exposedPorts := make(nat.PortSet)
|
exposedPorts := make(nat.PortSet)
|
||||||
for ep := range inspect.NetworkSettings.Ports {
|
for ep := range inspect.NetworkSettings.Ports {
|
||||||
splitp := strings.SplitN(ep, "/", 2)
|
port, proto, ok := strings.Cut(ep, "/")
|
||||||
if len(splitp) != 2 {
|
if !ok {
|
||||||
return nil, fmt.Errorf("PORT/PROTOCOL Format required for %q", ep)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,12 +49,12 @@ func ExecCreateHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
libpodConfig.Environment = make(map[string]string)
|
libpodConfig.Environment = make(map[string]string)
|
||||||
for _, envStr := range input.Env {
|
for _, envStr := range input.Env {
|
||||||
split := strings.SplitN(envStr, "=", 2)
|
key, val, hasVal := strings.Cut(envStr, "=")
|
||||||
if len(split) != 2 {
|
if !hasVal {
|
||||||
utils.Error(w, http.StatusBadRequest, fmt.Errorf("environment variable %q badly formed, must be key=value", envStr))
|
utils.Error(w, http.StatusBadRequest, fmt.Errorf("environment variable %q badly formed, must be key=value", envStr))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
libpodConfig.Environment[split[0]] = split[1]
|
libpodConfig.Environment[key] = val
|
||||||
}
|
}
|
||||||
libpodConfig.WorkDir = input.WorkingDir
|
libpodConfig.WorkDir = input.WorkingDir
|
||||||
libpodConfig.Privileged = input.Privileged
|
libpodConfig.Privileged = input.Privileged
|
||||||
|
|
|
||||||
|
|
@ -330,15 +330,15 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
|
||||||
if len(secretOpt) > 0 {
|
if len(secretOpt) > 0 {
|
||||||
modifiedOpt := []string{}
|
modifiedOpt := []string{}
|
||||||
for _, token := range secretOpt {
|
for _, token := range secretOpt {
|
||||||
arr := strings.SplitN(token, "=", 2)
|
key, val, hasVal := strings.Cut(token, "=")
|
||||||
if len(arr) > 1 {
|
if hasVal {
|
||||||
if arr[0] == "src" {
|
if key == "src" {
|
||||||
/* move secret away from contextDir */
|
/* move secret away from contextDir */
|
||||||
/* to make sure we dont accidentally commit temporary secrets to image*/
|
/* to make sure we dont accidentally commit temporary secrets to image*/
|
||||||
builderDirectory, _ := filepath.Split(contextDirectory)
|
builderDirectory, _ := filepath.Split(contextDirectory)
|
||||||
// following path is outside build context
|
// following path is outside build context
|
||||||
newSecretPath := filepath.Join(builderDirectory, arr[1])
|
newSecretPath := filepath.Join(builderDirectory, val)
|
||||||
oldSecretPath := filepath.Join(contextDirectory, arr[1])
|
oldSecretPath := filepath.Join(contextDirectory, val)
|
||||||
err := os.Rename(oldSecretPath, newSecretPath)
|
err := os.Rename(oldSecretPath, newSecretPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.BadRequest(w, "secrets", query.Secrets, err)
|
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"))
|
utils.BadRequest(w, "securityopt", query.SecurityOpt, errors.New("no-new-privileges is not supported"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
con := strings.SplitN(opt, "=", 2)
|
name, value, hasValue := strings.Cut(opt, "=")
|
||||||
if len(con) != 2 {
|
if !hasValue {
|
||||||
utils.BadRequest(w, "securityopt", query.SecurityOpt, fmt.Errorf("invalid --security-opt name=value pair: %q", opt))
|
utils.BadRequest(w, "securityopt", query.SecurityOpt, fmt.Errorf("invalid --security-opt name=value pair: %q", opt))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch con[0] {
|
switch name {
|
||||||
case "label":
|
case "label":
|
||||||
labelOpts = append(labelOpts, con[1])
|
labelOpts = append(labelOpts, value)
|
||||||
case "apparmor":
|
case "apparmor":
|
||||||
apparmor = con[1]
|
apparmor = value
|
||||||
case "seccomp":
|
case "seccomp":
|
||||||
seccomp = con[1]
|
seccomp = value
|
||||||
default:
|
default:
|
||||||
utils.BadRequest(w, "securityopt", query.SecurityOpt, fmt.Errorf("invalid --security-opt 2: %q", opt))
|
utils.BadRequest(w, "securityopt", query.SecurityOpt, fmt.Errorf("invalid --security-opt 2: %q", opt))
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -489,12 +489,12 @@ func ManifestModify(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
annotations := make(map[string]string)
|
annotations := make(map[string]string)
|
||||||
for _, annotationSpec := range body.ManifestAddOptions.Annotation {
|
for _, annotationSpec := range body.ManifestAddOptions.Annotation {
|
||||||
spec := strings.SplitN(annotationSpec, "=", 2)
|
key, val, hasVal := strings.Cut(annotationSpec, "=")
|
||||||
if len(spec) != 2 {
|
if !hasVal {
|
||||||
utils.Error(w, http.StatusBadRequest, fmt.Errorf("no value given for annotation %q", spec[0]))
|
utils.Error(w, http.StatusBadRequest, fmt.Errorf("no value given for annotation %q", key))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
annotations[spec[0]] = spec[1]
|
annotations[key] = val
|
||||||
}
|
}
|
||||||
body.ManifestAddOptions.Annotations = envLib.Join(body.ManifestAddOptions.Annotations, annotations)
|
body.ManifestAddOptions.Annotations = envLib.Join(body.ManifestAddOptions.Annotations, annotations)
|
||||||
body.ManifestAddOptions.Annotation = nil
|
body.ManifestAddOptions.Annotation = nil
|
||||||
|
|
|
||||||
|
|
@ -268,7 +268,7 @@ func normalizeAuthFileKey(authFileKey string) string {
|
||||||
stripped = strings.TrimPrefix(stripped, "https://")
|
stripped = strings.TrimPrefix(stripped, "https://")
|
||||||
|
|
||||||
if stripped != authFileKey { // URLs are interpreted to mean complete registries
|
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
|
// 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 {
|
if len(secretOpt) > 0 {
|
||||||
modifiedOpt := []string{}
|
modifiedOpt := []string{}
|
||||||
for _, token := range secretOpt {
|
for _, token := range secretOpt {
|
||||||
arr := strings.SplitN(token, "=", 2)
|
opt, val, hasVal := strings.Cut(token, "=")
|
||||||
if len(arr) > 1 {
|
if hasVal {
|
||||||
if arr[0] == "src" {
|
if opt == "src" {
|
||||||
// read specified secret into a tmp file
|
// read specified secret into a tmp file
|
||||||
// move tmp file to tar and change secret source to relative tmp file
|
// move tmp file to tar and change secret source to relative tmp file
|
||||||
tmpSecretFile, err := os.CreateTemp(options.ContextDirectory, "podman-build-secret")
|
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 os.Remove(tmpSecretFile.Name()) // clean up
|
||||||
defer tmpSecretFile.Close()
|
defer tmpSecretFile.Close()
|
||||||
srcSecretFile, err := os.Open(arr[1])
|
srcSecretFile, err := os.Open(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,9 @@ func parseUserInput(input string) (container string, path string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if spl := strings.SplitN(path, ":", 2); len(spl) == 2 {
|
if parsedContainer, parsedPath, ok := strings.Cut(path, ":"); ok {
|
||||||
container = spl[0]
|
container = parsedContainer
|
||||||
path = spl[1]
|
path = parsedPath
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,10 +98,10 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo
|
||||||
var imageNameWithoutTag string
|
var imageNameWithoutTag string
|
||||||
// Compare with ImageID, ImageName
|
// Compare with ImageID, ImageName
|
||||||
// Will match ImageName if running image has tag latest for other tags exact complete filter must be given
|
// Will match ImageName if running image has tag latest for other tags exact complete filter must be given
|
||||||
imageNameSlice := strings.SplitN(rootfsImageName, ":", 2)
|
name, tag, hasColon := strings.Cut(rootfsImageName, ":")
|
||||||
if len(imageNameSlice) == 2 {
|
if hasColon {
|
||||||
imageNameWithoutTag = imageNameSlice[0]
|
imageNameWithoutTag = name
|
||||||
imageTag = imageNameSlice[1]
|
imageTag = tag
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rootfsImageID == filterValue) ||
|
if (rootfsImageID == filterValue) ||
|
||||||
|
|
@ -144,13 +144,8 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo
|
||||||
//- volume=(<volume-name>|<mount-point-destination>)
|
//- volume=(<volume-name>|<mount-point-destination>)
|
||||||
return func(c *libpod.Container) bool {
|
return func(c *libpod.Container) bool {
|
||||||
containerConfig := c.ConfigNoCopy()
|
containerConfig := c.ConfigNoCopy()
|
||||||
var dest string
|
|
||||||
for _, filterValue := range filterValues {
|
for _, filterValue := range filterValues {
|
||||||
arr := strings.SplitN(filterValue, ":", 2)
|
source, dest, _ := strings.Cut(filterValue, ":")
|
||||||
source := arr[0]
|
|
||||||
if len(arr) == 2 {
|
|
||||||
dest = arr[1]
|
|
||||||
}
|
|
||||||
for _, mount := range containerConfig.Spec.Mounts {
|
for _, mount := range containerConfig.Spec.Mounts {
|
||||||
if dest != "" && (mount.Source == source && mount.Destination == dest) {
|
if dest != "" && (mount.Source == source && mount.Destination == dest) {
|
||||||
return true
|
return true
|
||||||
|
|
@ -233,19 +228,10 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo
|
||||||
// check if networkMode is configured as `container:<ctr>`
|
// check if networkMode is configured as `container:<ctr>`
|
||||||
// perform a match against filter `container:<IDorName>`
|
// perform a match against filter `container:<IDorName>`
|
||||||
// networks is already going to be empty if `container:<ctr>` is configured as Mode
|
// networks is already going to be empty if `container:<ctr>` is configured as Mode
|
||||||
if strings.HasPrefix(networkMode, "container:") {
|
if networkModeContainerID, ok := strings.CutPrefix(networkMode, "container:"); ok {
|
||||||
networkModeContainerPart := strings.SplitN(networkMode, ":", 2)
|
|
||||||
if len(networkModeContainerPart) < 2 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
networkModeContainerID := networkModeContainerPart[1]
|
|
||||||
for _, val := range filterValues {
|
for _, val := range filterValues {
|
||||||
if strings.HasPrefix(val, "container:") {
|
if idOrName, ok := strings.CutPrefix(val, "container:"); ok {
|
||||||
filterNetworkModePart := strings.SplitN(val, ":", 2)
|
filterNetworkModeIDorName := idOrName
|
||||||
if len(filterNetworkModePart) < 2 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
filterNetworkModeIDorName := filterNetworkModePart[1]
|
|
||||||
filterID, err := r.LookupContainerID(filterNetworkModeIDorName)
|
filterID, err := r.LookupContainerID(filterNetworkModeIDorName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
|
|
|
||||||
|
|
@ -49,14 +49,7 @@ func GenerateVolumeFilters(filter string, filterValues []string, runtime *libpod
|
||||||
case "opt":
|
case "opt":
|
||||||
return func(v *libpod.Volume) bool {
|
return func(v *libpod.Volume) bool {
|
||||||
for _, val := range filterValues {
|
for _, val := range filterValues {
|
||||||
filterArray := strings.SplitN(val, "=", 2)
|
filterKey, filterVal, _ := strings.Cut(val, "=")
|
||||||
filterKey := filterArray[0]
|
|
||||||
var filterVal string
|
|
||||||
if len(filterArray) > 1 {
|
|
||||||
filterVal = filterArray[1]
|
|
||||||
} else {
|
|
||||||
filterVal = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
for labelKey, labelValue := range v.Options() {
|
for labelKey, labelValue := range v.Options() {
|
||||||
if labelKey == filterKey && (filterVal == "" || labelValue == filterVal) {
|
if labelKey == filterKey && (filterVal == "" || labelValue == filterVal) {
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ func generateRunlabelCommand(runlabel string, img *libimage.Image, inputName str
|
||||||
name = splitImageName[len(splitImageName)-1]
|
name = splitImageName[len(splitImageName)-1]
|
||||||
// make sure to remove the tag from the image name, otherwise the name cannot
|
// 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
|
// 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).
|
// 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 {
|
if len(opts.Annotation) != 0 {
|
||||||
annotations := make(map[string]string)
|
annotations := make(map[string]string)
|
||||||
for _, annotationSpec := range opts.Annotation {
|
for _, annotationSpec := range opts.Annotation {
|
||||||
spec := strings.SplitN(annotationSpec, "=", 2)
|
key, val, hasVal := strings.Cut(annotationSpec, "=")
|
||||||
if len(spec) != 2 {
|
if !hasVal {
|
||||||
return "", fmt.Errorf("no value given for annotation %q", spec[0])
|
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)
|
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 {
|
if len(opts.Annotation) != 0 {
|
||||||
annotations := make(map[string]string)
|
annotations := make(map[string]string)
|
||||||
for _, annotationSpec := range opts.Annotation {
|
for _, annotationSpec := range opts.Annotation {
|
||||||
spec := strings.SplitN(annotationSpec, "=", 2)
|
key, val, hasVal := strings.Cut(annotationSpec, "=")
|
||||||
if len(spec) != 2 {
|
if !hasVal {
|
||||||
return "", fmt.Errorf("no value given for annotation %q", spec[0])
|
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)
|
opts.Annotations = envLib.Join(opts.Annotations, annotations)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,64 +27,64 @@ func VolumeOptions(opts map[string]string) ([]libpod.VolumeCreateOption, error)
|
||||||
for _, o := range splitVal {
|
for _, o := range splitVal {
|
||||||
// Options will be formatted as either "opt" or
|
// Options will be formatted as either "opt" or
|
||||||
// "opt=value"
|
// "opt=value"
|
||||||
splitO := strings.SplitN(o, "=", 2)
|
opt, val, hasVal := strings.Cut(o, "=")
|
||||||
switch strings.ToLower(splitO[0]) {
|
switch strings.ToLower(opt) {
|
||||||
case "size":
|
case "size":
|
||||||
size, err := units.FromHumanSize(splitO[1])
|
size, err := units.FromHumanSize(val)
|
||||||
if err != nil {
|
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)))
|
libpodOptions = append(libpodOptions, libpod.WithVolumeSize(uint64(size)))
|
||||||
finalVal = append(finalVal, o)
|
finalVal = append(finalVal, o)
|
||||||
// set option "SIZE": "$size"
|
// set option "SIZE": "$size"
|
||||||
volumeOptions["SIZE"] = splitO[1]
|
volumeOptions["SIZE"] = val
|
||||||
case "inodes":
|
case "inodes":
|
||||||
inodes, err := strconv.ParseUint(splitO[1], 10, 64)
|
inodes, err := strconv.ParseUint(val, 10, 64)
|
||||||
if err != nil {
|
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))
|
libpodOptions = append(libpodOptions, libpod.WithVolumeInodes(inodes))
|
||||||
finalVal = append(finalVal, o)
|
finalVal = append(finalVal, o)
|
||||||
// set option "INODES": "$size"
|
// set option "INODES": "$size"
|
||||||
volumeOptions["INODES"] = splitO[1]
|
volumeOptions["INODES"] = val
|
||||||
case "uid":
|
case "uid":
|
||||||
if len(splitO) != 2 {
|
if !hasVal {
|
||||||
return nil, fmt.Errorf("uid option must provide a UID: %w", define.ErrInvalidArg)
|
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 {
|
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)
|
logrus.Debugf("Removing uid= from options and adding WithVolumeUID for UID %d", intUID)
|
||||||
libpodOptions = append(libpodOptions, libpod.WithVolumeUID(intUID), libpod.WithVolumeNoChown())
|
libpodOptions = append(libpodOptions, libpod.WithVolumeUID(intUID), libpod.WithVolumeNoChown())
|
||||||
finalVal = append(finalVal, o)
|
finalVal = append(finalVal, o)
|
||||||
// set option "UID": "$uid"
|
// set option "UID": "$uid"
|
||||||
volumeOptions["UID"] = splitO[1]
|
volumeOptions["UID"] = val
|
||||||
case "gid":
|
case "gid":
|
||||||
if len(splitO) != 2 {
|
if !hasVal {
|
||||||
return nil, fmt.Errorf("gid option must provide a GID: %w", define.ErrInvalidArg)
|
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 {
|
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)
|
logrus.Debugf("Removing gid= from options and adding WithVolumeGID for GID %d", intGID)
|
||||||
libpodOptions = append(libpodOptions, libpod.WithVolumeGID(intGID), libpod.WithVolumeNoChown())
|
libpodOptions = append(libpodOptions, libpod.WithVolumeGID(intGID), libpod.WithVolumeNoChown())
|
||||||
finalVal = append(finalVal, o)
|
finalVal = append(finalVal, o)
|
||||||
// set option "GID": "$gid"
|
// set option "GID": "$gid"
|
||||||
volumeOptions["GID"] = splitO[1]
|
volumeOptions["GID"] = val
|
||||||
case "noquota":
|
case "noquota":
|
||||||
logrus.Debugf("Removing noquota from options and adding WithVolumeDisableQuota")
|
logrus.Debugf("Removing noquota from options and adding WithVolumeDisableQuota")
|
||||||
libpodOptions = append(libpodOptions, libpod.WithVolumeDisableQuota())
|
libpodOptions = append(libpodOptions, libpod.WithVolumeDisableQuota())
|
||||||
// set option "NOQUOTA": "true"
|
// set option "NOQUOTA": "true"
|
||||||
volumeOptions["NOQUOTA"] = "true"
|
volumeOptions["NOQUOTA"] = "true"
|
||||||
case "timeout":
|
case "timeout":
|
||||||
if len(splitO) != 2 {
|
if !hasVal {
|
||||||
return nil, fmt.Errorf("timeout option must provide a valid timeout in seconds: %w", define.ErrInvalidArg)
|
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 {
|
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 {
|
if intTimeout < 0 {
|
||||||
return nil, fmt.Errorf("volume timeout cannot be negative (got %d)", intTimeout)
|
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 {
|
if len(opts.Annotation) != 0 {
|
||||||
annotations := make(map[string]string)
|
annotations := make(map[string]string)
|
||||||
for _, annotationSpec := range opts.Annotation {
|
for _, annotationSpec := range opts.Annotation {
|
||||||
spec := strings.SplitN(annotationSpec, "=", 2)
|
key, val, hasVal := strings.Cut(annotationSpec, "=")
|
||||||
if len(spec) != 2 {
|
if !hasVal {
|
||||||
return "", fmt.Errorf("no value given for annotation %q", spec[0])
|
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)
|
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 {
|
if len(opts.Annotation) != 0 {
|
||||||
annotations := make(map[string]string)
|
annotations := make(map[string]string)
|
||||||
for _, annotationSpec := range opts.Annotation {
|
for _, annotationSpec := range opts.Annotation {
|
||||||
spec := strings.SplitN(annotationSpec, "=", 2)
|
key, val, hasVal := strings.Cut(annotationSpec, "=")
|
||||||
if len(spec) != 2 {
|
if !hasVal {
|
||||||
return "", fmt.Errorf("no value given for annotation %q", spec[0])
|
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)
|
opts.Annotations = envLib.Join(opts.Annotations, annotations)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -406,8 +406,8 @@ func GetServiceInformation(sshInfo *entities.ImageScpConnections, cliConnections
|
||||||
var urlS string
|
var urlS string
|
||||||
var iden string
|
var iden string
|
||||||
for i, val := range cliConnections {
|
for i, val := range cliConnections {
|
||||||
splitEnv := strings.SplitN(val, "::", 2)
|
connection, _, _ := strings.Cut(val, "::")
|
||||||
sshInfo.Connections = append(sshInfo.Connections, splitEnv[0])
|
sshInfo.Connections = append(sshInfo.Connections, connection)
|
||||||
conn, found := cfg.Engine.ServiceDestinations[sshInfo.Connections[i]]
|
conn, found := cfg.Engine.ServiceDestinations[sshInfo.Connections[i]]
|
||||||
if found {
|
if found {
|
||||||
urlS = conn.URI
|
urlS = conn.URI
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,8 @@ func ToLibpodFilters(f url.Values) (filters []string) {
|
||||||
func ToURLValues(f []string) (filters url.Values) {
|
func ToURLValues(f []string) (filters url.Values) {
|
||||||
filters = make(url.Values)
|
filters = make(url.Values)
|
||||||
for _, v := range f {
|
for _, v := range f {
|
||||||
t := strings.SplitN(v, "=", 2)
|
key, val, _ := strings.Cut(v, "=")
|
||||||
filters.Add(t[0], t[1])
|
filters.Add(key, val)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,14 +40,9 @@ func Slice(m map[string]string) []string {
|
||||||
// map.
|
// map.
|
||||||
func Map(slice []string) map[string]string {
|
func Map(slice []string) map[string]string {
|
||||||
envmap := make(map[string]string, len(slice))
|
envmap := make(map[string]string, len(slice))
|
||||||
for _, val := range slice {
|
for _, line := range slice {
|
||||||
data := strings.SplitN(val, "=", 2)
|
key, val, _ := strings.Cut(line, "=")
|
||||||
|
envmap[key] = val
|
||||||
if len(data) > 1 {
|
|
||||||
envmap[data[0]] = data[1]
|
|
||||||
} else {
|
|
||||||
envmap[data[0]] = ""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return envmap
|
return envmap
|
||||||
}
|
}
|
||||||
|
|
@ -94,26 +89,25 @@ func ParseFile(path string) (_ map[string]string, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseEnv(env map[string]string, line string) 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"
|
// catch invalid variables such as "=" or "=A"
|
||||||
if data[0] == "" {
|
if key == "" {
|
||||||
return fmt.Errorf("invalid variable: %q", line)
|
return fmt.Errorf("invalid variable: %q", line)
|
||||||
}
|
}
|
||||||
// trim the front of a variable, but nothing else
|
// trim the front of a variable, but nothing else
|
||||||
name := strings.TrimLeft(data[0], whiteSpaces)
|
name := strings.TrimLeft(key, whiteSpaces)
|
||||||
if len(data) > 1 {
|
if hasVal {
|
||||||
env[name] = data[1]
|
env[name] = val
|
||||||
} else {
|
} else {
|
||||||
if strings.HasSuffix(name, "*") {
|
if name, hasStar := strings.CutSuffix(name, "*"); hasStar {
|
||||||
name = strings.TrimSuffix(name, "*")
|
|
||||||
for _, e := range os.Environ() {
|
for _, e := range os.Environ() {
|
||||||
part := strings.SplitN(e, "=", 2)
|
envKey, envVal, hasEq := strings.Cut(e, "=")
|
||||||
if len(part) < 2 {
|
if !hasEq {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(part[0], name) {
|
if strings.HasPrefix(envKey, name) {
|
||||||
env[part[0]] = part[1]
|
env[envKey] = envVal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if val, ok := os.LookupEnv(name); ok {
|
} 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 looks like: {"BAR": "bar", "FOO": "foo"}
|
||||||
envs := make(map[string]string)
|
envs := make(map[string]string)
|
||||||
for _, env := range envsRawArr {
|
for _, env := range envsRawArr {
|
||||||
item := strings.SplitN(env, "=", 2)
|
key, value, _ := strings.Cut(env, "=")
|
||||||
envs[item[0]] = strings.Trim(item[1], "\"")
|
envs[key] = strings.Trim(value, "\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, test := range tests {
|
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
|
// NS gets the path associated with a ns:<path> cgroup ns
|
||||||
func (n CgroupMode) NS() string {
|
func (n CgroupMode) NS() string {
|
||||||
parts := strings.SplitN(string(n), ":", 2)
|
_, path, _ := strings.Cut(string(n), ":")
|
||||||
if len(parts) > 1 {
|
return path
|
||||||
return parts[1]
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsContainer indicates whether the container uses a new cgroup namespace.
|
// IsContainer indicates whether the container uses a new cgroup namespace.
|
||||||
func (n CgroupMode) IsContainer() bool {
|
func (n CgroupMode) IsContainer() bool {
|
||||||
parts := strings.SplitN(string(n), ":", 2)
|
typ, _, hasColon := strings.Cut(string(n), ":")
|
||||||
return len(parts) > 1 && parts[0] == containerType
|
return hasColon && typ == containerType
|
||||||
}
|
}
|
||||||
|
|
||||||
// Container returns the name of the container whose cgroup namespace is going to be used.
|
// Container returns the name of the container whose cgroup namespace is going to be used.
|
||||||
func (n CgroupMode) Container() string {
|
func (n CgroupMode) Container() string {
|
||||||
parts := strings.SplitN(string(n), ":", 2)
|
typ, name, hasName := strings.Cut(string(n), ":")
|
||||||
if len(parts) > 1 && parts[0] == containerType {
|
if hasName && typ == containerType {
|
||||||
return parts[1]
|
return name
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
@ -123,36 +120,36 @@ func (n UsernsMode) IsDefaultValue() bool {
|
||||||
// GetKeepIDOptions returns a KeepIDUserNsOptions with the settings to keepIDmatically set up
|
// GetKeepIDOptions returns a KeepIDUserNsOptions with the settings to keepIDmatically set up
|
||||||
// a user namespace.
|
// a user namespace.
|
||||||
func (n UsernsMode) GetKeepIDOptions() (*KeepIDUserNsOptions, error) {
|
func (n UsernsMode) GetKeepIDOptions() (*KeepIDUserNsOptions, error) {
|
||||||
parts := strings.SplitN(string(n), ":", 2)
|
nsmode, nsopts, hasOpts := strings.Cut(string(n), ":")
|
||||||
if parts[0] != "keep-id" {
|
if nsmode != "keep-id" {
|
||||||
return nil, fmt.Errorf("wrong user namespace mode")
|
return nil, fmt.Errorf("wrong user namespace mode")
|
||||||
}
|
}
|
||||||
options := KeepIDUserNsOptions{}
|
options := KeepIDUserNsOptions{}
|
||||||
if len(parts) == 1 {
|
if !hasOpts {
|
||||||
return &options, nil
|
return &options, nil
|
||||||
}
|
}
|
||||||
for _, o := range strings.Split(parts[1], ",") {
|
for _, o := range strings.Split(nsopts, ",") {
|
||||||
v := strings.SplitN(o, "=", 2)
|
opt, val, hasVal := strings.Cut(o, "=")
|
||||||
if len(v) != 2 {
|
if !hasVal {
|
||||||
return nil, fmt.Errorf("invalid option specified: %q", o)
|
return nil, fmt.Errorf("invalid option specified: %q", o)
|
||||||
}
|
}
|
||||||
switch v[0] {
|
switch opt {
|
||||||
case "uid":
|
case "uid":
|
||||||
s, err := strconv.ParseUint(v[1], 10, 32)
|
s, err := strconv.ParseUint(val, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
v := uint32(s)
|
v := uint32(s)
|
||||||
options.UID = &v
|
options.UID = &v
|
||||||
case "gid":
|
case "gid":
|
||||||
s, err := strconv.ParseUint(v[1], 10, 32)
|
s, err := strconv.ParseUint(val, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
v := uint32(s)
|
v := uint32(s)
|
||||||
options.GID = &v
|
options.GID = &v
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown option specified: %q", v[0])
|
return nil, fmt.Errorf("unknown option specified: %q", opt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &options, nil
|
return &options, nil
|
||||||
|
|
@ -185,24 +182,21 @@ func (n UsernsMode) IsNS() bool {
|
||||||
|
|
||||||
// NS gets the path associated with a ns:<path> userns ns
|
// NS gets the path associated with a ns:<path> userns ns
|
||||||
func (n UsernsMode) NS() string {
|
func (n UsernsMode) NS() string {
|
||||||
parts := strings.SplitN(string(n), ":", 2)
|
_, path, _ := strings.Cut(string(n), ":")
|
||||||
if len(parts) > 1 {
|
return path
|
||||||
return parts[1]
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsContainer indicates whether container uses a container userns.
|
// IsContainer indicates whether container uses a container userns.
|
||||||
func (n UsernsMode) IsContainer() bool {
|
func (n UsernsMode) IsContainer() bool {
|
||||||
parts := strings.SplitN(string(n), ":", 2)
|
typ, _, hasName := strings.Cut(string(n), ":")
|
||||||
return len(parts) > 1 && parts[0] == containerType
|
return hasName && typ == containerType
|
||||||
}
|
}
|
||||||
|
|
||||||
// Container is the id of the container which network this container is connected to.
|
// Container is the id of the container which network this container is connected to.
|
||||||
func (n UsernsMode) Container() string {
|
func (n UsernsMode) Container() string {
|
||||||
parts := strings.SplitN(string(n), ":", 2)
|
typ, name, hasName := strings.Cut(string(n), ":")
|
||||||
if len(parts) > 1 && parts[0] == containerType {
|
if hasName && typ == containerType {
|
||||||
return parts[1]
|
return name
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
@ -222,15 +216,15 @@ func (n UTSMode) IsHost() bool {
|
||||||
|
|
||||||
// IsContainer indicates whether the container uses a container's UTS namespace.
|
// IsContainer indicates whether the container uses a container's UTS namespace.
|
||||||
func (n UTSMode) IsContainer() bool {
|
func (n UTSMode) IsContainer() bool {
|
||||||
parts := strings.SplitN(string(n), ":", 2)
|
typ, _, hasName := strings.Cut(string(n), ":")
|
||||||
return len(parts) > 1 && parts[0] == containerType
|
return hasName && typ == containerType
|
||||||
}
|
}
|
||||||
|
|
||||||
// Container returns the name of the container whose uts namespace is going to be used.
|
// Container returns the name of the container whose uts namespace is going to be used.
|
||||||
func (n UTSMode) Container() string {
|
func (n UTSMode) Container() string {
|
||||||
parts := strings.SplitN(string(n), ":", 2)
|
typ, name, hasName := strings.Cut(string(n), ":")
|
||||||
if len(parts) > 1 && parts[0] == containerType {
|
if hasName && typ == containerType {
|
||||||
return parts[1]
|
return name
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
@ -270,8 +264,8 @@ func (n IpcMode) IsShareable() bool {
|
||||||
|
|
||||||
// IsContainer indicates whether the container uses another container's ipc namespace.
|
// IsContainer indicates whether the container uses another container's ipc namespace.
|
||||||
func (n IpcMode) IsContainer() bool {
|
func (n IpcMode) IsContainer() bool {
|
||||||
parts := strings.SplitN(string(n), ":", 2)
|
typ, _, hasName := strings.Cut(string(n), ":")
|
||||||
return len(parts) > 1 && parts[0] == containerType
|
return hasName && typ == containerType
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNone indicates whether container IpcMode is set to "none".
|
// 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.
|
// Container returns the name of the container ipc stack is going to be used.
|
||||||
func (n IpcMode) Container() string {
|
func (n IpcMode) Container() string {
|
||||||
parts := strings.SplitN(string(n), ":", 2)
|
typ, name, hasName := strings.Cut(string(n), ":")
|
||||||
if len(parts) > 1 && parts[0] == containerType {
|
if hasName && typ == containerType {
|
||||||
return parts[1]
|
return name
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
@ -313,8 +307,8 @@ func (n PidMode) IsHost() bool {
|
||||||
|
|
||||||
// IsContainer indicates whether the container uses a container's pid namespace.
|
// IsContainer indicates whether the container uses a container's pid namespace.
|
||||||
func (n PidMode) IsContainer() bool {
|
func (n PidMode) IsContainer() bool {
|
||||||
parts := strings.SplitN(string(n), ":", 2)
|
typ, _, hasName := strings.Cut(string(n), ":")
|
||||||
return len(parts) > 1 && parts[0] == containerType
|
return hasName && typ == containerType
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid indicates whether the pid namespace is valid.
|
// 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.
|
// Container returns the name of the container whose pid namespace is going to be used.
|
||||||
func (n PidMode) Container() string {
|
func (n PidMode) Container() string {
|
||||||
parts := strings.SplitN(string(n), ":", 2)
|
typ, name, hasName := strings.Cut(string(n), ":")
|
||||||
if len(parts) > 1 && parts[0] == containerType {
|
if hasName && typ == containerType {
|
||||||
return parts[1]
|
return name
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
@ -366,15 +360,15 @@ func (n NetworkMode) IsPrivate() bool {
|
||||||
|
|
||||||
// IsContainer indicates whether container uses a container network stack.
|
// IsContainer indicates whether container uses a container network stack.
|
||||||
func (n NetworkMode) IsContainer() bool {
|
func (n NetworkMode) IsContainer() bool {
|
||||||
parts := strings.SplitN(string(n), ":", 2)
|
typ, _, hasName := strings.Cut(string(n), ":")
|
||||||
return len(parts) > 1 && parts[0] == containerType
|
return hasName && typ == containerType
|
||||||
}
|
}
|
||||||
|
|
||||||
// Container is the id of the container which network this container is connected to.
|
// Container is the id of the container which network this container is connected to.
|
||||||
func (n NetworkMode) Container() string {
|
func (n NetworkMode) Container() string {
|
||||||
parts := strings.SplitN(string(n), ":", 2)
|
typ, name, hasName := strings.Cut(string(n), ":")
|
||||||
if len(parts) > 1 && parts[0] == containerType {
|
if hasName && typ == containerType {
|
||||||
return parts[1]
|
return name
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
@ -409,11 +403,8 @@ func (n NetworkMode) IsNS() bool {
|
||||||
|
|
||||||
// NS gets the path associated with a ns:<path> network ns
|
// NS gets the path associated with a ns:<path> network ns
|
||||||
func (n NetworkMode) NS() string {
|
func (n NetworkMode) NS() string {
|
||||||
parts := strings.SplitN(string(n), ":", 2)
|
_, path, _ := strings.Cut(string(n), ":")
|
||||||
if len(parts) > 1 {
|
return path
|
||||||
return parts[1]
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPod returns whether the network refers to pod networking
|
// 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]), ",")
|
entries := strings.Split(string(buf[:len]), ",")
|
||||||
res := make(map[string]uint64)
|
res := make(map[string]uint64)
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
kv := strings.SplitN(entry, "=", 2)
|
key, valstr, _ := strings.Cut(entry, "=")
|
||||||
key := kv[0]
|
val, err := strconv.ParseUint(valstr, 10, 0)
|
||||||
val, err := strconv.ParseUint(kv[1], 10, 0)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Warnf("unexpected rctl entry, ignoring: %s", entry)
|
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() {
|
for _, annotation := range rtc.Containers.Annotations.Get() {
|
||||||
split := strings.SplitN(v, "=", 2)
|
k, v, _ := strings.Cut(annotation, "=")
|
||||||
k := split[0]
|
|
||||||
v := ""
|
|
||||||
if len(split) == 2 {
|
|
||||||
v = split[1]
|
|
||||||
}
|
|
||||||
annotations[k] = v
|
annotations[k] = v
|
||||||
}
|
}
|
||||||
// now pass in the values from client
|
// 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 {
|
if conf.Spec.Process != nil && conf.Spec.Process.Env != nil {
|
||||||
env := make(map[string]string)
|
env := make(map[string]string)
|
||||||
for _, entry := range conf.Spec.Process.Env {
|
for _, entry := range conf.Spec.Process.Env {
|
||||||
split := strings.SplitN(entry, "=", 2)
|
key, val, hasVal := strings.Cut(entry, "=")
|
||||||
if len(split) == 2 {
|
if hasVal {
|
||||||
env[split[0]] = split[1]
|
env[key] = val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
specg.Env = env
|
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)
|
s.ResourceLimits.Unified = make(map[string]string)
|
||||||
}
|
}
|
||||||
for _, cgroupConf := range rtc.Containers.CgroupConf.Get() {
|
for _, cgroupConf := range rtc.Containers.CgroupConf.Get() {
|
||||||
cgr := strings.SplitN(cgroupConf, "=", 2)
|
key, val, hasVal := strings.Cut(cgroupConf, "=")
|
||||||
if len(cgr) != 2 {
|
if !hasVal {
|
||||||
return nil, nil, nil, fmt.Errorf("CgroupConf %q from containers.conf invalid, must be name=value", cgr)
|
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 {
|
if _, ok := s.ResourceLimits.Unified[key]; !ok {
|
||||||
s.ResourceLimits.Unified[cgr[0]] = cgr[1]
|
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)
|
s.LogConfiguration.Options = make(map[string]string)
|
||||||
for _, o := range opts.LogOptions {
|
for _, o := range opts.LogOptions {
|
||||||
split := strings.SplitN(o, "=", 2)
|
opt, val, hasVal := strings.Cut(o, "=")
|
||||||
if len(split) < 2 {
|
if !hasVal {
|
||||||
return nil, fmt.Errorf("invalid log option %q", o)
|
return nil, fmt.Errorf("invalid log option %q", o)
|
||||||
}
|
}
|
||||||
switch strings.ToLower(split[0]) {
|
switch strings.ToLower(opt) {
|
||||||
case "driver":
|
case "driver":
|
||||||
s.LogConfiguration.Driver = split[1]
|
s.LogConfiguration.Driver = val
|
||||||
case "path":
|
case "path":
|
||||||
s.LogConfiguration.Path = split[1]
|
s.LogConfiguration.Path = val
|
||||||
case "max-size":
|
case "max-size":
|
||||||
logSize, err := units.FromHumanSize(split[1])
|
logSize, err := units.FromHumanSize(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
s.LogConfiguration.Size = logSize
|
s.LogConfiguration.Size = logSize
|
||||||
default:
|
default:
|
||||||
switch len(split[1]) {
|
switch len(val) {
|
||||||
case 0:
|
case 0:
|
||||||
return nil, fmt.Errorf("invalid log option: %w", define.ErrInvalidArg)
|
return nil, fmt.Errorf("invalid log option: %w", define.ErrInvalidArg)
|
||||||
default:
|
default:
|
||||||
// tags for journald only
|
// tags for journald only
|
||||||
if s.LogConfiguration.Driver == "" || s.LogConfiguration.Driver == define.JournaldLogging {
|
if s.LogConfiguration.Driver == "" || s.LogConfiguration.Driver == define.JournaldLogging {
|
||||||
s.LogConfiguration.Options[split[0]] = split[1]
|
s.LogConfiguration.Options[opt] = val
|
||||||
} else {
|
} else {
|
||||||
logrus.Warnf("Can only set tags with journald log driver but driver is %q", s.LogConfiguration.Driver)
|
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
|
// Environment Variables
|
||||||
envs := map[string]string{}
|
envs := map[string]string{}
|
||||||
for _, env := range imageData.Config.Env {
|
for _, env := range imageData.Config.Env {
|
||||||
keyval := strings.SplitN(env, "=", 2)
|
key, val, _ := strings.Cut(env, "=")
|
||||||
envs[keyval[0]] = keyval[1]
|
envs[key] = val
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, env := range opts.Container.Env {
|
for _, env := range opts.Container.Env {
|
||||||
|
|
|
||||||
|
|
@ -262,9 +262,9 @@ func getVolumesFrom(volumesFrom []string, runtime *libpod.Runtime) (map[string]s
|
||||||
for _, volume := range volumesFrom {
|
for _, volume := range volumesFrom {
|
||||||
var options []string
|
var options []string
|
||||||
|
|
||||||
splitVol := strings.SplitN(volume, ":", 2)
|
idOrName, volOpts, hasVolOpts := strings.Cut(volume, ":")
|
||||||
if len(splitVol) == 2 {
|
if hasVolOpts {
|
||||||
splitOpts := strings.Split(splitVol[1], ",")
|
splitOpts := strings.Split(volOpts, ",")
|
||||||
setRORW := false
|
setRORW := false
|
||||||
setZ := false
|
setZ := false
|
||||||
for _, opt := range splitOpts {
|
for _, opt := range splitOpts {
|
||||||
|
|
@ -286,9 +286,9 @@ func getVolumesFrom(volumesFrom []string, runtime *libpod.Runtime) (map[string]s
|
||||||
options = splitOpts
|
options = splitOpts
|
||||||
}
|
}
|
||||||
|
|
||||||
ctr, err := runtime.LookupContainer(splitVol[0])
|
ctr, err := runtime.LookupContainer(idOrName)
|
||||||
if err != nil {
|
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())
|
logrus.Debugf("Adding volumes from container %s", ctr.ID())
|
||||||
|
|
|
||||||
|
|
@ -230,29 +230,23 @@ func (n *Namespace) validate() error {
|
||||||
// function.
|
// function.
|
||||||
func ParseNamespace(ns string) (Namespace, error) {
|
func ParseNamespace(ns string) (Namespace, error) {
|
||||||
toReturn := Namespace{}
|
toReturn := Namespace{}
|
||||||
switch {
|
switch ns {
|
||||||
case ns == "pod":
|
case "pod":
|
||||||
toReturn.NSMode = FromPod
|
toReturn.NSMode = FromPod
|
||||||
case ns == "host":
|
case "host":
|
||||||
toReturn.NSMode = Host
|
toReturn.NSMode = Host
|
||||||
case ns == "private", ns == "":
|
case "private", "":
|
||||||
toReturn.NSMode = 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:
|
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
|
return toReturn, nil
|
||||||
|
|
@ -302,37 +296,32 @@ func ParseIPCNamespace(ns string) (Namespace, error) {
|
||||||
// form.
|
// form.
|
||||||
func ParseUserNamespace(ns string) (Namespace, error) {
|
func ParseUserNamespace(ns string) (Namespace, error) {
|
||||||
toReturn := Namespace{}
|
toReturn := Namespace{}
|
||||||
switch {
|
switch ns {
|
||||||
case ns == "auto":
|
case "auto":
|
||||||
toReturn.NSMode = Auto
|
toReturn.NSMode = Auto
|
||||||
return toReturn, nil
|
return toReturn, nil
|
||||||
case strings.HasPrefix(ns, "auto:"):
|
case "keep-id":
|
||||||
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":
|
|
||||||
toReturn.NSMode = KeepID
|
toReturn.NSMode = KeepID
|
||||||
return toReturn, nil
|
return toReturn, nil
|
||||||
case strings.HasPrefix(ns, "keep-id:"):
|
case "nomap":
|
||||||
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":
|
|
||||||
toReturn.NSMode = NoMap
|
toReturn.NSMode = NoMap
|
||||||
return toReturn, nil
|
return toReturn, nil
|
||||||
case ns == "":
|
case "":
|
||||||
toReturn.NSMode = Host
|
toReturn.NSMode = Host
|
||||||
return toReturn, nil
|
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
|
// ParseNetworkFlag parses a network string slice into the network options
|
||||||
|
|
@ -352,10 +341,10 @@ func ParseNetworkFlag(networks []string, pastaNetworkNameExists bool) (Namespace
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case ns == string(Slirp), strings.HasPrefix(ns, string(Slirp)+":"):
|
case ns == string(Slirp), strings.HasPrefix(ns, string(Slirp)+":"):
|
||||||
parts := strings.SplitN(ns, ":", 2)
|
key, options, hasOptions := strings.Cut(ns, ":")
|
||||||
if len(parts) > 1 {
|
if hasOptions {
|
||||||
networkOptions = make(map[string][]string)
|
networkOptions = make(map[string][]string)
|
||||||
networkOptions[parts[0]] = strings.Split(parts[1], ",")
|
networkOptions[key] = strings.Split(options, ",")
|
||||||
}
|
}
|
||||||
toReturn.NSMode = Slirp
|
toReturn.NSMode = Slirp
|
||||||
case ns == string(FromPod):
|
case ns == string(FromPod):
|
||||||
|
|
@ -364,11 +353,11 @@ func ParseNetworkFlag(networks []string, pastaNetworkNameExists bool) (Namespace
|
||||||
toReturn.NSMode = Private
|
toReturn.NSMode = Private
|
||||||
case ns == string(Bridge), strings.HasPrefix(ns, string(Bridge)+":"):
|
case ns == string(Bridge), strings.HasPrefix(ns, string(Bridge)+":"):
|
||||||
toReturn.NSMode = Bridge
|
toReturn.NSMode = Bridge
|
||||||
parts := strings.SplitN(ns, ":", 2)
|
_, options, hasOptions := strings.Cut(ns, ":")
|
||||||
netOpts := types.PerNetworkOptions{}
|
netOpts := types.PerNetworkOptions{}
|
||||||
if len(parts) > 1 {
|
if hasOptions {
|
||||||
var err error
|
var err error
|
||||||
netOpts, err = parseBridgeNetworkOptions(parts[1])
|
netOpts, err = parseBridgeNetworkOptions(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return toReturn, nil, nil, err
|
return toReturn, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -381,30 +370,23 @@ func ParseNetworkFlag(networks []string, pastaNetworkNameExists bool) (Namespace
|
||||||
case ns == string(Host):
|
case ns == string(Host):
|
||||||
toReturn.NSMode = Host
|
toReturn.NSMode = Host
|
||||||
case strings.HasPrefix(ns, "ns:"):
|
case strings.HasPrefix(ns, "ns:"):
|
||||||
split := strings.SplitN(ns, ":", 2)
|
_, value, _ := strings.Cut(ns, ":")
|
||||||
if len(split) != 2 {
|
|
||||||
return toReturn, nil, nil, errors.New("must provide a path to a namespace when specifying \"ns:\"")
|
|
||||||
}
|
|
||||||
toReturn.NSMode = Path
|
toReturn.NSMode = Path
|
||||||
toReturn.Value = split[1]
|
toReturn.Value = value
|
||||||
case strings.HasPrefix(ns, string(FromContainer)+":"):
|
case strings.HasPrefix(ns, string(FromContainer)+":"):
|
||||||
split := strings.SplitN(ns, ":", 2)
|
_, value, _ := strings.Cut(ns, ":")
|
||||||
if len(split) != 2 {
|
|
||||||
return toReturn, nil, nil, errors.New("must provide name or ID or a container when specifying \"container:\"")
|
|
||||||
}
|
|
||||||
toReturn.NSMode = FromContainer
|
toReturn.NSMode = FromContainer
|
||||||
toReturn.Value = split[1]
|
toReturn.Value = value
|
||||||
case ns == string(Pasta), strings.HasPrefix(ns, string(Pasta)+":"):
|
case ns == string(Pasta), strings.HasPrefix(ns, string(Pasta)+":"):
|
||||||
var parts []string
|
key, options, hasOptions := strings.Cut(ns, ":")
|
||||||
|
|
||||||
if pastaNetworkNameExists {
|
if pastaNetworkNameExists {
|
||||||
goto nextCase
|
goto nextCase
|
||||||
}
|
}
|
||||||
|
|
||||||
parts = strings.SplitN(ns, ":", 2)
|
if hasOptions {
|
||||||
if len(parts) > 1 {
|
|
||||||
networkOptions = make(map[string][]string)
|
networkOptions = make(map[string][]string)
|
||||||
networkOptions[parts[0]] = strings.Split(parts[1], ",")
|
networkOptions[key] = strings.Split(options, ",")
|
||||||
}
|
}
|
||||||
toReturn.NSMode = Pasta
|
toReturn.NSMode = Pasta
|
||||||
break
|
break
|
||||||
|
|
@ -412,22 +394,22 @@ func ParseNetworkFlag(networks []string, pastaNetworkNameExists bool) (Namespace
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
// we should have a normal network
|
// we should have a normal network
|
||||||
parts := strings.SplitN(ns, ":", 2)
|
name, options, hasOptions := strings.Cut(ns, ":")
|
||||||
if len(parts) == 1 {
|
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.
|
// Assume we have been given a comma separated list of networks for backwards compat.
|
||||||
networkList := strings.Split(ns, ",")
|
networkList := strings.Split(ns, ",")
|
||||||
for _, net := range networkList {
|
for _, net := range networkList {
|
||||||
podmanNetworks[net] = types.PerNetworkOptions{}
|
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
|
// networks need bridge mode
|
||||||
|
|
@ -440,24 +422,24 @@ func ParseNetworkFlag(networks []string, pastaNetworkNameExists bool) (Namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, network := range networks[1:] {
|
for _, network := range networks[1:] {
|
||||||
parts := strings.SplitN(network, ":", 2)
|
name, options, hasOptions := strings.Cut(network, ":")
|
||||||
if parts[0] == "" {
|
if name == "" {
|
||||||
return toReturn, nil, nil, fmt.Errorf("network name cannot be empty: %w", define.ErrInvalidArg)
|
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
|
// 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),
|
if slices.Contains([]string{string(Bridge), string(Slirp), string(FromPod), string(NoNetwork),
|
||||||
string(Default), string(Private), string(Path), string(FromContainer), string(Host)}, parts[0]) {
|
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", parts[0], define.ErrInvalidArg)
|
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{}
|
netOpts := types.PerNetworkOptions{}
|
||||||
if len(parts) > 1 {
|
if hasOptions {
|
||||||
var err error
|
var err error
|
||||||
netOpts, err = parseBridgeNetworkOptions(parts[1])
|
netOpts, err = parseBridgeNetworkOptions(options)
|
||||||
if err != nil {
|
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, ",")
|
allopts := strings.Split(opts, ",")
|
||||||
for _, opt := range allopts {
|
for _, opt := range allopts {
|
||||||
split := strings.SplitN(opt, "=", 2)
|
name, value, _ := strings.Cut(opt, "=")
|
||||||
switch split[0] {
|
switch name {
|
||||||
case "ip", "ip6":
|
case "ip", "ip6":
|
||||||
ip := net.ParseIP(split[1])
|
ip := net.ParseIP(value)
|
||||||
if ip == nil {
|
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)
|
netOpts.StaticIPs = append(netOpts.StaticIPs, ip)
|
||||||
|
|
||||||
case "mac":
|
case "mac":
|
||||||
mac, err := net.ParseMAC(split[1])
|
mac, err := net.ParseMAC(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return netOpts, err
|
return netOpts, err
|
||||||
}
|
}
|
||||||
netOpts.StaticMAC = types.HardwareAddr(mac)
|
netOpts.StaticMAC = types.HardwareAddr(mac)
|
||||||
|
|
||||||
case "alias":
|
case "alias":
|
||||||
if split[1] == "" {
|
if value == "" {
|
||||||
return netOpts, errors.New("alias cannot be empty")
|
return netOpts, errors.New("alias cannot be empty")
|
||||||
}
|
}
|
||||||
netOpts.Aliases = append(netOpts.Aliases, split[1])
|
netOpts.Aliases = append(netOpts.Aliases, value)
|
||||||
|
|
||||||
case "interface_name":
|
case "interface_name":
|
||||||
if split[1] == "" {
|
if value == "" {
|
||||||
return netOpts, errors.New("interface_name cannot be empty")
|
return netOpts, errors.New("interface_name cannot be empty")
|
||||||
}
|
}
|
||||||
netOpts.InterfaceName = split[1]
|
netOpts.InterfaceName = value
|
||||||
|
|
||||||
default:
|
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
|
return netOpts, nil
|
||||||
|
|
|
||||||
|
|
@ -502,11 +502,11 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
|
||||||
|
|
||||||
// Last, add user annotations
|
// Last, add user annotations
|
||||||
for _, annotation := range c.Annotation {
|
for _, annotation := range c.Annotation {
|
||||||
splitAnnotation := strings.SplitN(annotation, "=", 2)
|
key, val, hasVal := strings.Cut(annotation, "=")
|
||||||
if len(splitAnnotation) < 2 {
|
if !hasVal {
|
||||||
return errors.New("annotations must be formatted KEY=VALUE")
|
return errors.New("annotations must be formatted KEY=VALUE")
|
||||||
}
|
}
|
||||||
annotations[splitAnnotation[0]] = splitAnnotation[1]
|
annotations[key] = val
|
||||||
}
|
}
|
||||||
if len(s.Annotations) == 0 {
|
if len(s.Annotations) == 0 {
|
||||||
s.Annotations = annotations
|
s.Annotations = annotations
|
||||||
|
|
@ -515,11 +515,11 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
|
||||||
if len(c.StorageOpts) > 0 {
|
if len(c.StorageOpts) > 0 {
|
||||||
opts := make(map[string]string, len(c.StorageOpts))
|
opts := make(map[string]string, len(c.StorageOpts))
|
||||||
for _, opt := range c.StorageOpts {
|
for _, opt := range c.StorageOpts {
|
||||||
split := strings.SplitN(opt, "=", 2)
|
key, val, hasVal := strings.Cut(opt, "=")
|
||||||
if len(split) != 2 {
|
if !hasVal {
|
||||||
return errors.New("storage-opt must be formatted KEY=VALUE")
|
return errors.New("storage-opt must be formatted KEY=VALUE")
|
||||||
}
|
}
|
||||||
opts[split[0]] = split[1]
|
opts[key] = val
|
||||||
}
|
}
|
||||||
s.StorageOpts = opts
|
s.StorageOpts = opts
|
||||||
}
|
}
|
||||||
|
|
@ -673,11 +673,11 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
|
||||||
// check if key=value and convert
|
// check if key=value and convert
|
||||||
sysmap := make(map[string]string)
|
sysmap := make(map[string]string)
|
||||||
for _, ctl := range c.Sysctl {
|
for _, ctl := range c.Sysctl {
|
||||||
splitCtl := strings.SplitN(ctl, "=", 2)
|
key, val, hasVal := strings.Cut(ctl, "=")
|
||||||
if len(splitCtl) < 2 {
|
if !hasVal {
|
||||||
return fmt.Errorf("invalid sysctl value %q", ctl)
|
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 {
|
if len(s.Sysctl) == 0 || len(c.Sysctl) != 0 {
|
||||||
s.Sysctl = sysmap
|
s.Sysctl = sysmap
|
||||||
|
|
@ -690,48 +690,51 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
|
||||||
for _, opt := range c.SecurityOpt {
|
for _, opt := range c.SecurityOpt {
|
||||||
// Docker deprecated the ":" syntax but still supports it,
|
// Docker deprecated the ":" syntax but still supports it,
|
||||||
// so we need to as well
|
// so we need to as well
|
||||||
var con []string
|
var key, val string
|
||||||
|
var hasVal bool
|
||||||
if strings.Contains(opt, "=") {
|
if strings.Contains(opt, "=") {
|
||||||
con = strings.SplitN(opt, "=", 2)
|
key, val, hasVal = strings.Cut(opt, "=")
|
||||||
} else {
|
} else {
|
||||||
con = strings.SplitN(opt, ":", 2)
|
key, val, hasVal = strings.Cut(opt, ":")
|
||||||
}
|
}
|
||||||
if len(con) != 2 &&
|
if !hasVal &&
|
||||||
con[0] != "no-new-privileges" {
|
key != "no-new-privileges" {
|
||||||
return fmt.Errorf("invalid --security-opt 1: %q", opt)
|
return fmt.Errorf("invalid --security-opt 1: %q", opt)
|
||||||
}
|
}
|
||||||
switch con[0] {
|
switch key {
|
||||||
case "apparmor":
|
case "apparmor":
|
||||||
s.ContainerSecurityConfig.ApparmorProfile = con[1]
|
s.ContainerSecurityConfig.ApparmorProfile = val
|
||||||
s.Annotations[define.InspectAnnotationApparmor] = con[1]
|
s.Annotations[define.InspectAnnotationApparmor] = val
|
||||||
case "label":
|
case "label":
|
||||||
if con[1] == "nested" {
|
if val == "nested" {
|
||||||
s.ContainerSecurityConfig.LabelNested = true
|
s.ContainerSecurityConfig.LabelNested = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// TODO selinux opts and label opts are the same thing
|
// 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=")
|
s.Annotations[define.InspectAnnotationLabel] = strings.Join(s.ContainerSecurityConfig.SelinuxOpts, ",label=")
|
||||||
case "mask":
|
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":
|
case "proc-opts":
|
||||||
s.ProcOpts = strings.Split(con[1], ",")
|
s.ProcOpts = strings.Split(val, ",")
|
||||||
case "seccomp":
|
case "seccomp":
|
||||||
s.SeccompProfilePath = con[1]
|
s.SeccompProfilePath = val
|
||||||
s.Annotations[define.InspectAnnotationSeccomp] = con[1]
|
s.Annotations[define.InspectAnnotationSeccomp] = val
|
||||||
// this option is for docker compatibility, it is the same as unmask=ALL
|
// this option is for docker compatibility, it is the same as unmask=ALL
|
||||||
case "systempaths":
|
case "systempaths":
|
||||||
if con[1] == "unconfined" {
|
if val == "unconfined" {
|
||||||
s.ContainerSecurityConfig.Unmask = append(s.ContainerSecurityConfig.Unmask, []string{"ALL"}...)
|
s.ContainerSecurityConfig.Unmask = append(s.ContainerSecurityConfig.Unmask, []string{"ALL"}...)
|
||||||
} else {
|
} 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":
|
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":
|
case "no-new-privileges":
|
||||||
noNewPrivileges := true
|
noNewPrivileges := true
|
||||||
if len(con) == 2 {
|
if hasVal {
|
||||||
noNewPrivileges, err = strconv.ParseBool(con[1])
|
noNewPrivileges, err = strconv.ParseBool(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid --security-opt 2: %q", opt)
|
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)
|
logOpts := make(map[string]string)
|
||||||
for _, o := range c.LogOptions {
|
for _, o := range c.LogOptions {
|
||||||
split := strings.SplitN(o, "=", 2)
|
key, val, hasVal := strings.Cut(o, "=")
|
||||||
if len(split) < 2 {
|
if !hasVal {
|
||||||
return fmt.Errorf("invalid log option %q", o)
|
return fmt.Errorf("invalid log option %q", o)
|
||||||
}
|
}
|
||||||
switch strings.ToLower(split[0]) {
|
switch strings.ToLower(key) {
|
||||||
case "driver":
|
case "driver":
|
||||||
s.LogConfiguration.Driver = split[1]
|
s.LogConfiguration.Driver = val
|
||||||
case "path":
|
case "path":
|
||||||
s.LogConfiguration.Path = split[1]
|
s.LogConfiguration.Path = val
|
||||||
case "max-size":
|
case "max-size":
|
||||||
logSize, err := units.FromHumanSize(split[1])
|
logSize, err := units.FromHumanSize(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.LogConfiguration.Size = logSize
|
s.LogConfiguration.Size = logSize
|
||||||
default:
|
default:
|
||||||
logOpts[split[0]] = split[1]
|
logOpts[key] = val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(s.LogConfiguration.Options) == 0 || len(c.LogOptions) != 0 {
|
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) {
|
func parseWeightDevices(weightDevs []string) (map[string]specs.LinuxWeightDevice, error) {
|
||||||
wd := make(map[string]specs.LinuxWeightDevice)
|
wd := make(map[string]specs.LinuxWeightDevice)
|
||||||
for _, val := range weightDevs {
|
for _, dev := range weightDevs {
|
||||||
split := strings.SplitN(val, ":", 2)
|
key, val, hasVal := strings.Cut(dev, ":")
|
||||||
if len(split) != 2 {
|
if !hasVal {
|
||||||
return nil, fmt.Errorf("bad format: %s", val)
|
return nil, fmt.Errorf("bad format: %s", dev)
|
||||||
}
|
}
|
||||||
if !strings.HasPrefix(split[0], "/dev/") {
|
if !strings.HasPrefix(key, "/dev/") {
|
||||||
return nil, fmt.Errorf("bad format for device path: %s", val)
|
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 {
|
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) {
|
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)
|
w := uint16(weight)
|
||||||
wd[split[0]] = specs.LinuxWeightDevice{
|
wd[key] = specs.LinuxWeightDevice{
|
||||||
Weight: &w,
|
Weight: &w,
|
||||||
LeafWeight: nil,
|
LeafWeight: nil,
|
||||||
}
|
}
|
||||||
|
|
@ -1030,41 +1033,41 @@ func parseWeightDevices(weightDevs []string) (map[string]specs.LinuxWeightDevice
|
||||||
|
|
||||||
func parseThrottleBPSDevices(bpsDevices []string) (map[string]specs.LinuxThrottleDevice, error) {
|
func parseThrottleBPSDevices(bpsDevices []string) (map[string]specs.LinuxThrottleDevice, error) {
|
||||||
td := make(map[string]specs.LinuxThrottleDevice)
|
td := make(map[string]specs.LinuxThrottleDevice)
|
||||||
for _, val := range bpsDevices {
|
for _, dev := range bpsDevices {
|
||||||
split := strings.SplitN(val, ":", 2)
|
key, val, hasVal := strings.Cut(dev, ":")
|
||||||
if len(split) != 2 {
|
if !hasVal {
|
||||||
return nil, fmt.Errorf("bad format: %s", val)
|
return nil, fmt.Errorf("bad format: %s", dev)
|
||||||
}
|
}
|
||||||
if !strings.HasPrefix(split[0], "/dev/") {
|
if !strings.HasPrefix(key, "/dev/") {
|
||||||
return nil, fmt.Errorf("bad format for device path: %s", val)
|
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 {
|
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 {
|
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
|
return td, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseThrottleIOPsDevices(iopsDevices []string) (map[string]specs.LinuxThrottleDevice, error) {
|
func parseThrottleIOPsDevices(iopsDevices []string) (map[string]specs.LinuxThrottleDevice, error) {
|
||||||
td := make(map[string]specs.LinuxThrottleDevice)
|
td := make(map[string]specs.LinuxThrottleDevice)
|
||||||
for _, val := range iopsDevices {
|
for _, dev := range iopsDevices {
|
||||||
split := strings.SplitN(val, ":", 2)
|
key, val, hasVal := strings.Cut(dev, ":")
|
||||||
if len(split) != 2 {
|
if !hasVal {
|
||||||
return nil, fmt.Errorf("bad format: %s", val)
|
return nil, fmt.Errorf("bad format: %s", dev)
|
||||||
}
|
}
|
||||||
if !strings.HasPrefix(split[0], "/dev/") {
|
if !strings.HasPrefix(key, "/dev/") {
|
||||||
return nil, fmt.Errorf("bad format for device path: %s", val)
|
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 {
|
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
|
return td, nil
|
||||||
}
|
}
|
||||||
|
|
@ -1103,42 +1106,42 @@ func parseSecrets(secrets []string) ([]specgen.Secret, map[string]string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, val := range split {
|
for _, val := range split {
|
||||||
kv := strings.SplitN(val, "=", 2)
|
name, value, hasValue := strings.Cut(val, "=")
|
||||||
if len(kv) < 2 {
|
if !hasValue {
|
||||||
return nil, nil, fmt.Errorf("option %s must be in form option=value: %w", val, secretParseError)
|
return nil, nil, fmt.Errorf("option %s must be in form option=value: %w", val, secretParseError)
|
||||||
}
|
}
|
||||||
switch kv[0] {
|
switch name {
|
||||||
case "source":
|
case "source":
|
||||||
source = kv[1]
|
source = value
|
||||||
case "type":
|
case "type":
|
||||||
if secretType != "" {
|
if secretType != "" {
|
||||||
return nil, nil, fmt.Errorf("cannot set more than one secret type: %w", secretParseError)
|
return nil, nil, fmt.Errorf("cannot set more than one secret type: %w", secretParseError)
|
||||||
}
|
}
|
||||||
if kv[1] != "mount" && kv[1] != "env" {
|
if value != "mount" && value != "env" {
|
||||||
return nil, nil, fmt.Errorf("type %s is invalid: %w", kv[1], secretParseError)
|
return nil, nil, fmt.Errorf("type %s is invalid: %w", value, secretParseError)
|
||||||
}
|
}
|
||||||
secretType = kv[1]
|
secretType = value
|
||||||
case "target":
|
case "target":
|
||||||
target = kv[1]
|
target = value
|
||||||
case "mode":
|
case "mode":
|
||||||
mountOnly = true
|
mountOnly = true
|
||||||
mode64, err := strconv.ParseUint(kv[1], 8, 32)
|
mode64, err := strconv.ParseUint(value, 8, 32)
|
||||||
if err != nil {
|
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)
|
mode = uint32(mode64)
|
||||||
case "uid", "UID":
|
case "uid", "UID":
|
||||||
mountOnly = true
|
mountOnly = true
|
||||||
uid64, err := strconv.ParseUint(kv[1], 10, 32)
|
uid64, err := strconv.ParseUint(value, 10, 32)
|
||||||
if err != nil {
|
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)
|
uid = uint32(uid64)
|
||||||
case "gid", "GID":
|
case "gid", "GID":
|
||||||
mountOnly = true
|
mountOnly = true
|
||||||
gid64, err := strconv.ParseUint(kv[1], 10, 32)
|
gid64, err := strconv.ParseUint(value, 10, 32)
|
||||||
if err != nil {
|
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)
|
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)
|
return specs.LinuxDeviceCgroup{}, fmt.Errorf("invalid device type in device-access-add: %s", devType)
|
||||||
}
|
}
|
||||||
|
|
||||||
number := strings.SplitN(value[1], ":", 2)
|
majorNumber, minorNumber, hasMinor := strings.Cut(value[1], ":")
|
||||||
if number[0] != "*" {
|
if majorNumber != "*" {
|
||||||
i, err := strconv.ParseUint(number[0], 10, 64)
|
i, err := strconv.ParseUint(majorNumber, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return specs.LinuxDeviceCgroup{}, err
|
return specs.LinuxDeviceCgroup{}, err
|
||||||
}
|
}
|
||||||
m := int64(i)
|
m := int64(i)
|
||||||
major = &m
|
major = &m
|
||||||
}
|
}
|
||||||
if len(number) == 2 && number[1] != "*" {
|
if hasMinor && minorNumber != "*" {
|
||||||
i, err := strconv.ParseUint(number[1], 10, 64)
|
i, err := strconv.ParseUint(minorNumber, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return specs.LinuxDeviceCgroup{}, err
|
return specs.LinuxDeviceCgroup{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -1263,11 +1266,11 @@ func GetResources(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions)
|
||||||
|
|
||||||
unifieds := make(map[string]string)
|
unifieds := make(map[string]string)
|
||||||
for _, unified := range c.CgroupConf {
|
for _, unified := range c.CgroupConf {
|
||||||
splitUnified := strings.SplitN(unified, "=", 2)
|
key, val, hasVal := strings.Cut(unified, "=")
|
||||||
if len(splitUnified) < 2 {
|
if !hasVal {
|
||||||
return nil, errors.New("--cgroup-conf must be formatted KEY=VALUE")
|
return nil, errors.New("--cgroup-conf must be formatted KEY=VALUE")
|
||||||
}
|
}
|
||||||
unifieds[splitUnified[0]] = splitUnified[1]
|
unifieds[key] = val
|
||||||
}
|
}
|
||||||
if len(unifieds) > 0 {
|
if len(unifieds) > 0 {
|
||||||
s.ResourceLimits.Unified = unifieds
|
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
|
var setTmpcopyup, setRORW, setSuid, setDev, setExec, setRelabel, setOwnership, setSwap bool
|
||||||
|
|
||||||
mnt := spec.Mount{}
|
mnt := spec.Mount{}
|
||||||
for _, val := range args {
|
for _, arg := range args {
|
||||||
kv := strings.SplitN(val, "=", 2)
|
name, value, hasValue := strings.Cut(arg, "=")
|
||||||
switch kv[0] {
|
switch name {
|
||||||
case "bind-nonrecursive":
|
case "bind-nonrecursive":
|
||||||
if mountType != define.TypeBind {
|
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)
|
mnt.Options = append(mnt.Options, define.TypeBind)
|
||||||
case "bind-propagation":
|
case "bind-propagation":
|
||||||
if mountType != define.TypeBind {
|
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 {
|
if !hasValue {
|
||||||
return nil, fmt.Errorf("%v: %w", kv[0], errOptionArg)
|
return nil, fmt.Errorf("%v: %w", name, errOptionArg)
|
||||||
}
|
}
|
||||||
mnt.Options = append(mnt.Options, kv[1])
|
mnt.Options = append(mnt.Options, value)
|
||||||
case "consistency":
|
case "consistency":
|
||||||
// Often used on MACs and mistakenly on Linux platforms.
|
// Often used on MACs and mistakenly on Linux platforms.
|
||||||
// Since Docker ignores this option so shall we.
|
// Since Docker ignores this option so shall we.
|
||||||
continue
|
continue
|
||||||
case "idmap":
|
case "idmap":
|
||||||
if len(kv) > 1 {
|
if hasValue {
|
||||||
mnt.Options = append(mnt.Options, fmt.Sprintf("idmap=%s", kv[1]))
|
mnt.Options = append(mnt.Options, fmt.Sprintf("idmap=%s", value))
|
||||||
} else {
|
} else {
|
||||||
mnt.Options = append(mnt.Options, "idmap")
|
mnt.Options = append(mnt.Options, "idmap")
|
||||||
}
|
}
|
||||||
|
|
@ -294,42 +294,41 @@ func parseMountOptions(mountType string, args []string) (*spec.Mount, error) {
|
||||||
// ro=[true|false]
|
// ro=[true|false]
|
||||||
// rw
|
// rw
|
||||||
// rw=[true|false]
|
// rw=[true|false]
|
||||||
if kv[0] == "readonly" {
|
if name == "readonly" {
|
||||||
kv[0] = "ro"
|
name = "ro"
|
||||||
}
|
}
|
||||||
switch len(kv) {
|
if hasValue {
|
||||||
case 1:
|
switch strings.ToLower(value) {
|
||||||
mnt.Options = append(mnt.Options, kv[0])
|
|
||||||
case 2:
|
|
||||||
switch strings.ToLower(kv[1]) {
|
|
||||||
case "true":
|
case "true":
|
||||||
mnt.Options = append(mnt.Options, kv[0])
|
mnt.Options = append(mnt.Options, name)
|
||||||
case "false":
|
case "false":
|
||||||
// Set the opposite only for rw
|
// Set the opposite only for rw
|
||||||
// ro's opposite is the default
|
// ro's opposite is the default
|
||||||
if kv[0] == "rw" {
|
if name == "rw" {
|
||||||
mnt.Options = append(mnt.Options, "ro")
|
mnt.Options = append(mnt.Options, "ro")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
mnt.Options = append(mnt.Options, name)
|
||||||
}
|
}
|
||||||
case "nodev", "dev":
|
case "nodev", "dev":
|
||||||
if setDev {
|
if setDev {
|
||||||
return nil, fmt.Errorf("cannot pass 'nodev' and 'dev' mnt.Options more than once: %w", errOptionArg)
|
return nil, fmt.Errorf("cannot pass 'nodev' and 'dev' mnt.Options more than once: %w", errOptionArg)
|
||||||
}
|
}
|
||||||
setDev = true
|
setDev = true
|
||||||
mnt.Options = append(mnt.Options, kv[0])
|
mnt.Options = append(mnt.Options, name)
|
||||||
case "noexec", "exec":
|
case "noexec", "exec":
|
||||||
if setExec {
|
if setExec {
|
||||||
return nil, fmt.Errorf("cannot pass 'noexec' and 'exec' mnt.Options more than once: %w", errOptionArg)
|
return nil, fmt.Errorf("cannot pass 'noexec' and 'exec' mnt.Options more than once: %w", errOptionArg)
|
||||||
}
|
}
|
||||||
setExec = true
|
setExec = true
|
||||||
mnt.Options = append(mnt.Options, kv[0])
|
mnt.Options = append(mnt.Options, name)
|
||||||
case "nosuid", "suid":
|
case "nosuid", "suid":
|
||||||
if setSuid {
|
if setSuid {
|
||||||
return nil, fmt.Errorf("cannot pass 'nosuid' and 'suid' mnt.Options more than once: %w", errOptionArg)
|
return nil, fmt.Errorf("cannot pass 'nosuid' and 'suid' mnt.Options more than once: %w", errOptionArg)
|
||||||
}
|
}
|
||||||
setSuid = true
|
setSuid = true
|
||||||
mnt.Options = append(mnt.Options, kv[0])
|
mnt.Options = append(mnt.Options, name)
|
||||||
case "noswap":
|
case "noswap":
|
||||||
if setSwap {
|
if setSwap {
|
||||||
return nil, fmt.Errorf("cannot pass 'noswap' mnt.Options more than once: %w", errOptionArg)
|
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)
|
return nil, fmt.Errorf("the 'noswap' option is only allowed with rootful tmpfs mounts: %w", errOptionArg)
|
||||||
}
|
}
|
||||||
setSwap = true
|
setSwap = true
|
||||||
mnt.Options = append(mnt.Options, kv[0])
|
mnt.Options = append(mnt.Options, name)
|
||||||
case "relabel":
|
case "relabel":
|
||||||
if setRelabel {
|
if setRelabel {
|
||||||
return nil, fmt.Errorf("cannot pass 'relabel' option more than once: %w", errOptionArg)
|
return nil, fmt.Errorf("cannot pass 'relabel' option more than once: %w", errOptionArg)
|
||||||
}
|
}
|
||||||
setRelabel = true
|
setRelabel = true
|
||||||
if len(kv) != 2 {
|
if !hasValue {
|
||||||
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)
|
||||||
}
|
}
|
||||||
switch kv[1] {
|
switch value {
|
||||||
case "private":
|
case "private":
|
||||||
mnt.Options = append(mnt.Options, "Z")
|
mnt.Options = append(mnt.Options, "Z")
|
||||||
case "shared":
|
case "shared":
|
||||||
mnt.Options = append(mnt.Options, "z")
|
mnt.Options = append(mnt.Options, "z")
|
||||||
default:
|
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":
|
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":
|
case "src", "source":
|
||||||
if mountType == define.TypeTmpfs {
|
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 != "" {
|
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 {
|
if !hasValue {
|
||||||
return nil, fmt.Errorf("%v: %w", kv[0], errOptionArg)
|
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)
|
return nil, fmt.Errorf("host directory cannot be empty: %w", errOptionArg)
|
||||||
}
|
}
|
||||||
mnt.Source = kv[1]
|
mnt.Source = value
|
||||||
case "target", "dst", "destination":
|
case "target", "dst", "destination":
|
||||||
if mnt.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 {
|
if !hasValue {
|
||||||
return nil, fmt.Errorf("%v: %w", kv[0], errOptionArg)
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
mnt.Destination = unixPathClean(kv[1])
|
mnt.Destination = unixPathClean(value)
|
||||||
case "tmpcopyup", "notmpcopyup":
|
case "tmpcopyup", "notmpcopyup":
|
||||||
if mountType != define.TypeTmpfs {
|
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 {
|
if setTmpcopyup {
|
||||||
return nil, fmt.Errorf("cannot pass 'tmpcopyup' and 'notmpcopyup' mnt.Options more than once: %w", errOptionArg)
|
return nil, fmt.Errorf("cannot pass 'tmpcopyup' and 'notmpcopyup' mnt.Options more than once: %w", errOptionArg)
|
||||||
}
|
}
|
||||||
setTmpcopyup = true
|
setTmpcopyup = true
|
||||||
mnt.Options = append(mnt.Options, kv[0])
|
mnt.Options = append(mnt.Options, name)
|
||||||
case "tmpfs-mode":
|
case "tmpfs-mode":
|
||||||
if mountType != define.TypeTmpfs {
|
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 {
|
if !hasValue {
|
||||||
return nil, fmt.Errorf("%v: %w", kv[0], errOptionArg)
|
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":
|
case "tmpfs-size":
|
||||||
if mountType != define.TypeTmpfs {
|
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 {
|
if !hasValue {
|
||||||
return nil, fmt.Errorf("%v: %w", kv[0], errOptionArg)
|
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":
|
case "U", "chown":
|
||||||
if setOwnership {
|
if setOwnership {
|
||||||
return nil, fmt.Errorf("cannot pass 'U' or 'chown' option more than once: %w", errOptionArg)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -421,16 +420,16 @@ func parseMountOptions(mountType string, args []string) (*spec.Mount, error) {
|
||||||
setOwnership = true
|
setOwnership = true
|
||||||
case "volume-label":
|
case "volume-label":
|
||||||
if mountType != define.TypeVolume {
|
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")
|
return nil, fmt.Errorf("the --volume-label option is not presently implemented")
|
||||||
case "volume-opt":
|
case "volume-opt":
|
||||||
if mountType != define.TypeVolume {
|
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:
|
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 {
|
if mountType != "glob" && len(mnt.Destination) == 0 {
|
||||||
|
|
@ -534,22 +533,22 @@ func getDevptsMount(args []string) (spec.Mount, error) {
|
||||||
|
|
||||||
var setDest bool
|
var setDest bool
|
||||||
|
|
||||||
for _, val := range args {
|
for _, arg := range args {
|
||||||
kv := strings.SplitN(val, "=", 2)
|
name, value, hasValue := strings.Cut(arg, "=")
|
||||||
switch kv[0] {
|
switch name {
|
||||||
case "uid", "gid", "mode", "ptxmode", "newinstance", "max":
|
case "uid", "gid", "mode", "ptxmode", "newinstance", "max":
|
||||||
newMount.Options = append(newMount.Options, val)
|
newMount.Options = append(newMount.Options, arg)
|
||||||
case "target", "dst", "destination":
|
case "target", "dst", "destination":
|
||||||
if len(kv) == 1 {
|
if !hasValue {
|
||||||
return newMount, fmt.Errorf("%v: %w", kv[0], errOptionArg)
|
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
|
return newMount, err
|
||||||
}
|
}
|
||||||
newMount.Destination = unixPathClean(kv[1])
|
newMount.Destination = unixPathClean(value)
|
||||||
setDest = true
|
setDest = true
|
||||||
default:
|
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) {
|
func getImageVolume(args []string) (*specgen.ImageVolume, error) {
|
||||||
newVolume := new(specgen.ImageVolume)
|
newVolume := new(specgen.ImageVolume)
|
||||||
|
|
||||||
for _, val := range args {
|
for _, arg := range args {
|
||||||
kv := strings.SplitN(val, "=", 2)
|
name, value, hasValue := strings.Cut(arg, "=")
|
||||||
switch kv[0] {
|
switch name {
|
||||||
case "src", "source":
|
case "src", "source":
|
||||||
if len(kv) == 1 {
|
if !hasValue {
|
||||||
return nil, fmt.Errorf("%v: %w", kv[0], errOptionArg)
|
return nil, fmt.Errorf("%v: %w", name, errOptionArg)
|
||||||
}
|
}
|
||||||
newVolume.Source = kv[1]
|
newVolume.Source = value
|
||||||
case "target", "dst", "destination":
|
case "target", "dst", "destination":
|
||||||
if len(kv) == 1 {
|
if !hasValue {
|
||||||
return nil, fmt.Errorf("%v: %w", kv[0], errOptionArg)
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
newVolume.Destination = unixPathClean(kv[1])
|
newVolume.Destination = unixPathClean(value)
|
||||||
case "rw", "readwrite":
|
case "rw", "readwrite":
|
||||||
switch kv[1] {
|
switch value {
|
||||||
case "true":
|
case "true":
|
||||||
newVolume.ReadWrite = true
|
newVolume.ReadWrite = true
|
||||||
case "false":
|
case "false":
|
||||||
// Nothing to do. RO is default.
|
// Nothing to do. RO is default.
|
||||||
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":
|
case "consistency":
|
||||||
// Often used on MACs and mistakenly on Linux platforms.
|
// Often used on MACs and mistakenly on Linux platforms.
|
||||||
// Since Docker ignores this option so shall we.
|
// Since Docker ignores this option so shall we.
|
||||||
continue
|
continue
|
||||||
default:
|
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
|
// validChownFlag ensures that the U or chown flag is correctly used
|
||||||
func validChownFlag(flag string) (bool, error) {
|
func validChownFlag(value string) (bool, error) {
|
||||||
kv := strings.SplitN(flag, "=", 2)
|
// U=[true|false]
|
||||||
switch len(kv) {
|
switch {
|
||||||
case 1:
|
case strings.EqualFold(value, "true"), value == "":
|
||||||
case 2:
|
return true, nil
|
||||||
// U=[true|false]
|
case strings.EqualFold(value, "false"):
|
||||||
switch strings.ToLower(kv[1]) {
|
return false, nil
|
||||||
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)
|
|
||||||
}
|
|
||||||
default:
|
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
|
// Use path instead of filepath to preserve Unix style paths on Windows
|
||||||
|
|
|
||||||
|
|
@ -13,41 +13,41 @@ func Test_validChownFlag(t *testing.T) {
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "U true",
|
name: "lower-case true",
|
||||||
args: args{
|
args: args{
|
||||||
flag: "U=true",
|
flag: "true",
|
||||||
},
|
},
|
||||||
want: true,
|
want: true,
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "U true case does not matter",
|
name: "case-insensitive true",
|
||||||
args: args{
|
args: args{
|
||||||
flag: "u=True",
|
flag: "True",
|
||||||
},
|
},
|
||||||
want: true,
|
want: true,
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "U is false",
|
name: "lower-case false",
|
||||||
args: args{
|
args: args{
|
||||||
flag: "U=false",
|
flag: "false",
|
||||||
},
|
},
|
||||||
want: false,
|
want: false,
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "chown should also work",
|
name: "case-insensitive false",
|
||||||
args: args{
|
args: args{
|
||||||
flag: "chown=true",
|
flag: "falsE",
|
||||||
},
|
},
|
||||||
want: true,
|
want: false,
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "garbage value should fail",
|
name: "garbage value should fail",
|
||||||
args: args{
|
args: args{
|
||||||
flag: "U=foobar",
|
flag: "foobar",
|
||||||
},
|
},
|
||||||
want: false,
|
want: false,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
|
|
|
||||||
|
|
@ -471,8 +471,8 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst
|
||||||
// because it does try to red the value from the environment
|
// because it does try to red the value from the environment
|
||||||
if !strings.Contains(env, "=") {
|
if !strings.Contains(env, "=") {
|
||||||
for _, containerEnv := range info.containerEnv {
|
for _, containerEnv := range info.containerEnv {
|
||||||
split := strings.SplitN(containerEnv, "=", 2)
|
key, _, _ := strings.Cut(containerEnv, "=")
|
||||||
if split[0] == env {
|
if key == env {
|
||||||
info.ExtraEnvs = append(info.ExtraEnvs, escapeSystemdArg(containerEnv))
|
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 {
|
for _, update := range updateMaps {
|
||||||
annotation := fmt.Sprintf("--annotation=%s", autoUpdateLabel)
|
annotation := fmt.Sprintf("--annotation=%s", autoUpdateLabel)
|
||||||
updateType := update
|
updateType := update
|
||||||
val := strings.SplitN(update, "/", 2)
|
annoValue, typ, hasSlash := strings.Cut(update, "/")
|
||||||
if len(val) == 2 {
|
if hasSlash {
|
||||||
annotation = annotation + "/" + val[0]
|
annotation = annotation + "/" + annoValue
|
||||||
updateType = val[1]
|
updateType = typ
|
||||||
}
|
}
|
||||||
execStart.addf("%s=%s", annotation, updateType)
|
execStart.addf("%s=%s", annotation, updateType)
|
||||||
}
|
}
|
||||||
|
|
@ -1741,13 +1741,13 @@ func resolveContainerMountParams(containerUnitFile, serviceUnitFile *parser.Unit
|
||||||
sourceIndex := -1
|
sourceIndex := -1
|
||||||
originalSource := ""
|
originalSource := ""
|
||||||
for i, token := range tokens {
|
for i, token := range tokens {
|
||||||
kv := strings.SplitN(token, "=", 2)
|
key, val, hasVal := strings.Cut(token, "=")
|
||||||
if kv[0] == "source" || kv[0] == "src" {
|
if key == "source" || key == "src" {
|
||||||
if len(kv) < 2 {
|
if !hasVal {
|
||||||
return "", fmt.Errorf("source parameter does not include a value")
|
return "", fmt.Errorf("source parameter does not include a value")
|
||||||
}
|
}
|
||||||
sourceIndex = i
|
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) {
|
func parseTimestamp(value string) (int64, int64, error) {
|
||||||
sa := strings.SplitN(value, ".", 2)
|
spart, npart, hasParts := strings.Cut(value, ".")
|
||||||
s, err := strconv.ParseInt(sa[0], 10, 64)
|
s, err := strconv.ParseInt(spart, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return s, 0, err
|
return s, 0, err
|
||||||
}
|
}
|
||||||
if len(sa) != 2 {
|
if !hasParts {
|
||||||
return s, 0, nil
|
return s, 0, nil
|
||||||
}
|
}
|
||||||
n, err := strconv.ParseInt(sa[1], 10, 64)
|
n, err := strconv.ParseInt(npart, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return s, n, err
|
return s, n, err
|
||||||
}
|
}
|
||||||
// should already be in nanoseconds but just in case convert n to nanoseconds
|
// 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
|
return s, n, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -131,8 +131,11 @@ func parseUids(colonDelimitKeys []byte) []string {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
parseduid := uid
|
parseduid := uid
|
||||||
if strings.Contains(uid, "<") && strings.Contains(uid, ">") {
|
if ltidx := strings.Index(uid, "<"); ltidx != -1 {
|
||||||
parseduid = strings.SplitN(strings.SplitAfterN(uid, "<", 2)[1], ">", 2)[0]
|
subuid := parseduid[ltidx+1:]
|
||||||
|
if gtidx := strings.Index(subuid, ">"); gtidx != -1 {
|
||||||
|
parseduid = subuid[:gtidx]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
parseduids = append(parseduids, parseduid)
|
parseduids = append(parseduids, parseduid)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,9 +65,9 @@ func PrepareFilters(r *http.Request) (*map[string][]string, error) {
|
||||||
}
|
}
|
||||||
filterMap := map[string][]string{}
|
filterMap := map[string][]string{}
|
||||||
for _, filter := range filtersList {
|
for _, filter := range filtersList {
|
||||||
split := strings.SplitN(filter, "=", 2)
|
fname, filter, hasFilter := strings.Cut(filter, "=")
|
||||||
if len(split) > 1 {
|
if hasFilter {
|
||||||
filterMap[split[0]] = append(filterMap[split[0]], split[1])
|
filterMap[fname] = append(filterMap[fname], filter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &filterMap, nil
|
return &filterMap, nil
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string
|
||||||
newOptions := make([]string, 0, len(options))
|
newOptions := make([]string, 0, len(options))
|
||||||
for _, opt := range options {
|
for _, opt := range options {
|
||||||
// Some options have parameters - size, mode
|
// 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
|
// add advanced options such as upperdir=/path and workdir=/path, when overlay is specified
|
||||||
if foundOverlay {
|
if foundOverlay {
|
||||||
|
|
@ -47,11 +47,11 @@ func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(splitOpt[0], "subpath") {
|
if strings.HasPrefix(key, "subpath") {
|
||||||
newOptions = append(newOptions, opt)
|
newOptions = append(newOptions, opt)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(splitOpt[0], "idmap") {
|
if strings.HasPrefix(key, "idmap") {
|
||||||
if foundIdmap {
|
if foundIdmap {
|
||||||
return nil, fmt.Errorf("the 'idmap' option can only be set once: %w", ErrDupeMntOption)
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
switch splitOpt[0] {
|
switch key {
|
||||||
case "copy", "nocopy":
|
case "copy", "nocopy":
|
||||||
if foundCopy {
|
if foundCopy {
|
||||||
return nil, fmt.Errorf("only one of 'nocopy' and 'copy' can be used: %w", ErrDupeMntOption)
|
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) {
|
func ParseDriverOpts(option string) (string, string, error) {
|
||||||
token := strings.SplitN(option, "=", 2)
|
_, val, hasVal := strings.Cut(option, "=")
|
||||||
if len(token) != 2 {
|
if !hasVal {
|
||||||
return "", "", fmt.Errorf("cannot parse driver opts: %w", ErrBadMntOption)
|
return "", "", fmt.Errorf("cannot parse driver opts: %w", ErrBadMntOption)
|
||||||
}
|
}
|
||||||
opt := strings.SplitN(token[1], "=", 2)
|
optKey, optVal, hasOptVal := strings.Cut(val, "=")
|
||||||
if len(opt) != 2 {
|
if !hasOptVal {
|
||||||
return "", "", fmt.Errorf("cannot parse driver opts: %w", ErrBadMntOption)
|
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
|
// Helper function to determine the username/password passed
|
||||||
// in the creds string. It could be either or both.
|
// in the creds string. It could be either or both.
|
||||||
func parseCreds(creds string) (string, string) {
|
func parseCreds(creds string) (string, string) {
|
||||||
if creds == "" {
|
username, password, _ := strings.Cut(creds, ":")
|
||||||
return "", ""
|
return username, password
|
||||||
}
|
|
||||||
up := strings.SplitN(creds, ":", 2)
|
|
||||||
if len(up) == 1 {
|
|
||||||
return up[0], ""
|
|
||||||
}
|
|
||||||
return up[0], up[1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Takes build context and validates `.containerignore` or `.dockerignore`
|
// 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
|
// GetAutoOptions returns an AutoUserNsOptions with the settings to automatically set up
|
||||||
// a user namespace.
|
// a user namespace.
|
||||||
func GetAutoOptions(n namespaces.UsernsMode) (*stypes.AutoUserNsOptions, error) {
|
func GetAutoOptions(n namespaces.UsernsMode) (*stypes.AutoUserNsOptions, error) {
|
||||||
parts := strings.SplitN(string(n), ":", 2)
|
mode, opts, hasOpts := strings.Cut(string(n), ":")
|
||||||
if parts[0] != "auto" {
|
if mode != "auto" {
|
||||||
return nil, fmt.Errorf("wrong user namespace mode")
|
return nil, fmt.Errorf("wrong user namespace mode")
|
||||||
}
|
}
|
||||||
options := stypes.AutoUserNsOptions{}
|
options := stypes.AutoUserNsOptions{}
|
||||||
if len(parts) == 1 {
|
if !hasOpts {
|
||||||
return &options, nil
|
return &options, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -937,32 +931,32 @@ func GetAutoOptions(n namespaces.UsernsMode) (*stypes.AutoUserNsOptions, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, o := range strings.Split(parts[1], ",") {
|
for _, o := range strings.Split(opts, ",") {
|
||||||
v := strings.SplitN(o, "=", 2)
|
key, val, hasVal := strings.Cut(o, "=")
|
||||||
if len(v) != 2 {
|
if !hasVal {
|
||||||
return nil, fmt.Errorf("invalid option specified: %q", o)
|
return nil, fmt.Errorf("invalid option specified: %q", o)
|
||||||
}
|
}
|
||||||
switch v[0] {
|
switch key {
|
||||||
case "size":
|
case "size":
|
||||||
s, err := strconv.ParseUint(v[1], 10, 32)
|
s, err := strconv.ParseUint(val, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
options.Size = uint32(s)
|
options.Size = uint32(s)
|
||||||
case "uidmapping":
|
case "uidmapping":
|
||||||
mapping, err := parseAutoIDMap(v[1], "UID", parentUIDMap)
|
mapping, err := parseAutoIDMap(val, "UID", parentUIDMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
options.AdditionalUIDMappings = append(options.AdditionalUIDMappings, mapping...)
|
options.AdditionalUIDMappings = append(options.AdditionalUIDMappings, mapping...)
|
||||||
case "gidmapping":
|
case "gidmapping":
|
||||||
mapping, err := parseAutoIDMap(v[1], "GID", parentGIDMap)
|
mapping, err := parseAutoIDMap(val, "GID", parentGIDMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
options.AdditionalGIDMappings = append(options.AdditionalGIDMappings, mapping...)
|
options.AdditionalGIDMappings = append(options.AdditionalGIDMappings, mapping...)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown option specified: %q", v[0])
|
return nil, fmt.Errorf("unknown option specified: %q", key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &options, nil
|
return &options, nil
|
||||||
|
|
@ -1077,9 +1071,9 @@ func getTomlStorage(storeOptions *stypes.StoreOptions) *tomlConfig {
|
||||||
config.Storage.RunRoot = storeOptions.RunRoot
|
config.Storage.RunRoot = storeOptions.RunRoot
|
||||||
config.Storage.GraphRoot = storeOptions.GraphRoot
|
config.Storage.GraphRoot = storeOptions.GraphRoot
|
||||||
for _, i := range storeOptions.GraphDriverOptions {
|
for _, i := range storeOptions.GraphDriverOptions {
|
||||||
s := strings.SplitN(i, "=", 2)
|
program, hasPrefix := strings.CutPrefix(i, "overlay.mount_program=")
|
||||||
if s[0] == "overlay.mount_program" && len(s) == 2 {
|
if hasPrefix {
|
||||||
config.Storage.Options.MountProgram = s[1]
|
config.Storage.Options.MountProgram = program
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -209,12 +209,11 @@ var _ = SynchronizedAfterSuite(func() {
|
||||||
scanner := bufio.NewScanner(f)
|
scanner := bufio.NewScanner(f)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
text := scanner.Text()
|
text := scanner.Text()
|
||||||
timing := strings.SplitN(text, "\t\t", 2)
|
name, durationString, ok := strings.Cut(text, "\t\t")
|
||||||
if len(timing) != 2 {
|
if !ok {
|
||||||
Fail(fmt.Sprintf("incorrect timing line: %q", text))
|
Fail(fmt.Sprintf("incorrect timing line: %q", text))
|
||||||
}
|
}
|
||||||
name := timing[0]
|
duration, err := strconv.ParseFloat(durationString, 64)
|
||||||
duration, err := strconv.ParseFloat(timing[1], 64)
|
|
||||||
Expect(err).ToNot(HaveOccurred(), "failed to parse float from timings file")
|
Expect(err).ToNot(HaveOccurred(), "failed to parse float from timings file")
|
||||||
testTimings = append(testTimings, testResult{name: name, length: duration})
|
testTimings = append(testTimings, testResult{name: name, length: duration})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -202,12 +202,8 @@ func keyValueStringToMap(keyValueString, separator string) (map[string]string, e
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, param := range keyVarList[0] {
|
for _, param := range keyVarList[0] {
|
||||||
val := ""
|
key, val, _ := strings.Cut(param, "=")
|
||||||
kv := strings.SplitN(param, "=", 2)
|
keyValMap[key] = val
|
||||||
if len(kv) == 2 {
|
|
||||||
val = kv[1]
|
|
||||||
}
|
|
||||||
keyValMap[kv[0]] = val
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return keyValMap, nil
|
return keyValMap, nil
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue