Merge pull request #44381 from thaJeztah/strings_cut

Replace uses of `strings.Split(N)` with `strings.Cut()`
This commit is contained in:
Tianon Gravi 2022-12-21 09:16:05 -08:00 committed by GitHub
commit 3a5598affa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 323 additions and 405 deletions

View file

@ -166,13 +166,13 @@ func (args Args) MatchKVList(key string, sources map[string]string) bool {
}
for value := range fieldValues {
testKV := strings.SplitN(value, "=", 2)
testK, testV, hasValue := strings.Cut(value, "=")
v, ok := sources[testKV[0]]
v, ok := sources[testK]
if !ok {
return false
}
if len(testKV) == 2 && testKV[1] != v {
if hasValue && testV != v {
return false
}
}

View file

@ -105,27 +105,27 @@ func GetTimestamp(value string, reference time.Time) (string, error) {
// since := time.Unix(seconds, nanoseconds)
//
// returns seconds as defaultSeconds if value == ""
func ParseTimestamps(value string, defaultSeconds int64) (int64, int64, error) {
func ParseTimestamps(value string, defaultSeconds int64) (seconds int64, nanoseconds int64, err error) {
if value == "" {
return defaultSeconds, 0, nil
}
return parseTimestamp(value)
}
func parseTimestamp(value string) (int64, int64, error) {
sa := strings.SplitN(value, ".", 2)
s, err := strconv.ParseInt(sa[0], 10, 64)
func parseTimestamp(value string) (sec int64, nsec int64, err error) {
s, n, ok := strings.Cut(value, ".")
sec, err = strconv.ParseInt(s, 10, 64)
if err != nil {
return s, 0, err
return sec, 0, err
}
if len(sa) != 2 {
return s, 0, nil
if !ok {
return sec, 0, nil
}
n, err := strconv.ParseInt(sa[1], 10, 64)
nsec, err = strconv.ParseInt(n, 10, 64)
if err != nil {
return s, n, err
return sec, nsec, err
}
// should already be in nanoseconds but just in case convert n to nanoseconds
n = int64(float64(n) * math.Pow(float64(10), float64(9-len(sa[1]))))
return s, n, nil
nsec = int64(float64(nsec) * math.Pow(float64(10), float64(9-len(n))))
return sec, nsec, nil
}

View file

@ -348,20 +348,19 @@ func DecodeSecurityOptions(opts []string) ([]SecurityOpt, error) {
continue
}
secopt := SecurityOpt{}
split := strings.Split(opt, ",")
for _, s := range split {
kv := strings.SplitN(s, "=", 2)
if len(kv) != 2 {
for _, s := range strings.Split(opt, ",") {
k, v, ok := strings.Cut(s, "=")
if !ok {
return nil, fmt.Errorf("invalid security option %q", s)
}
if kv[0] == "" || kv[1] == "" {
if k == "" || v == "" {
return nil, errors.New("invalid empty security option")
}
if kv[0] == "name" {
secopt.Name = kv[1]
if k == "name" {
secopt.Name = v
continue
}
secopt.Options = append(secopt.Options, KeyValue{Key: kv[0], Value: kv[1]})
secopt.Options = append(secopt.Options, KeyValue{Key: k, Value: v})
}
so = append(so, secopt)
}

View file

@ -46,8 +46,7 @@ func dispatchEnv(ctx context.Context, d dispatchRequest, c *instructions.EnvComm
commitMessage.WriteString(" " + newVar)
gotOne := false
for i, envVar := range runConfig.Env {
envParts := strings.SplitN(envVar, "=", 2)
compareFrom := envParts[0]
compareFrom, _, _ := strings.Cut(envVar, "=")
if shell.EqualEnvKeys(compareFrom, name) {
runConfig.Env[i] = newVar
gotOne = true
@ -408,9 +407,9 @@ func dispatchRun(ctx context.Context, d dispatchRequest, c *instructions.RunComm
// These args are transparent so resulting image should be the same regardless
// of the value.
func prependEnvOnCmd(buildArgs *BuildArgs, buildArgVars []string, cmd strslice.StrSlice) strslice.StrSlice {
var tmpBuildEnv []string
tmpBuildEnv := make([]string, 0, len(buildArgVars))
for _, env := range buildArgVars {
key := strings.SplitN(env, "=", 2)[0]
key, _, _ := strings.Cut(env, "=")
if buildArgs.IsReferencedOrNotBuiltin(key) {
tmpBuildEnv = append(tmpBuildEnv, env)
}
@ -418,7 +417,7 @@ func prependEnvOnCmd(buildArgs *BuildArgs, buildArgVars []string, cmd strslice.S
sort.Strings(tmpBuildEnv)
tmpEnv := append([]string{fmt.Sprintf("|%d", len(tmpBuildEnv))}, tmpBuildEnv...)
return strslice.StrSlice(append(tmpEnv, cmd...))
return append(tmpEnv, cmd...)
}
// CMD foo

View file

@ -97,15 +97,10 @@ func parseRemoteURL(remoteURL string) (gitRepo, error) {
remoteURL = "https://" + remoteURL
}
var fragment string
if strings.HasPrefix(remoteURL, "git@") {
// git@.. is not an URL, so cannot be parsed as URL
parts := strings.SplitN(remoteURL, "#", 2)
repo.remote = parts[0]
if len(parts) == 2 {
fragment = parts[1]
}
var fragment string
repo.remote, fragment, _ = strings.Cut(remoteURL, "#")
repo.ref, repo.subdir = getRefAndSubdir(fragment)
} else {
u, err := url.Parse(remoteURL)
@ -126,15 +121,11 @@ func parseRemoteURL(remoteURL string) (gitRepo, error) {
}
func getRefAndSubdir(fragment string) (ref string, subdir string) {
refAndDir := strings.SplitN(fragment, ":", 2)
ref = "master"
if len(refAndDir[0]) != 0 {
ref = refAndDir[0]
ref, subdir, _ = strings.Cut(fragment, ":")
if ref == "" {
ref = "master"
}
if len(refAndDir) > 1 && len(refAndDir[1]) != 0 {
subdir = refAndDir[1]
}
return
return ref, subdir
}
func fetchArgs(remoteURL string, ref string) []string {

View file

@ -282,13 +282,12 @@ func (cli *Client) HTTPClient() *http.Client {
// ParseHostURL parses a url string, validates the string is a host url, and
// returns the parsed URL
func ParseHostURL(host string) (*url.URL, error) {
protoAddrParts := strings.SplitN(host, "://", 2)
if len(protoAddrParts) == 1 {
proto, addr, ok := strings.Cut(host, "://")
if !ok || addr == "" {
return nil, errors.Errorf("unable to parse docker host `%s`", host)
}
var basePath string
proto, addr := protoAddrParts[0], protoAddrParts[1]
if proto == "tcp" {
parsed, err := url.Parse("tcp://" + addr)
if err != nil {

View file

@ -64,10 +64,10 @@ func parsePingResponse(cli *Client, resp serverResponse) (types.Ping, error) {
ping.BuilderVersion = types.BuilderVersion(bv)
}
if si := resp.header.Get("Swarm"); si != "" {
parts := strings.SplitN(si, "/", 2)
state, role, _ := strings.Cut(si, "/")
ping.SwarmStatus = &swarm.Status{
NodeState: swarm.LocalNodeState(parts[0]),
ControlAvailable: len(parts) == 2 && parts[1] == "manager",
NodeState: swarm.LocalNodeState(state),
ControlAvailable: role == "manager",
}
}
err := cli.checkResponseErr(resp)

View file

@ -662,13 +662,11 @@ func loadListeners(cli *DaemonCli, serverConfig *apiserver.Config) ([]string, er
for i := 0; i < len(serverConfig.Hosts); i++ {
protoAddr := serverConfig.Hosts[i]
protoAddrParts := strings.SplitN(serverConfig.Hosts[i], "://", 2)
if len(protoAddrParts) != 2 {
proto, addr, ok := strings.Cut(protoAddr, "://")
if !ok || addr == "" {
return nil, fmt.Errorf("bad format %s, expected PROTO://ADDR", protoAddr)
}
proto, addr := protoAddrParts[0], protoAddrParts[1]
// It's a bad idea to bind to TCP without tlsverify.
authEnabled := serverConfig.TLSConfig != nil && serverConfig.TLSConfig.ClientAuth == tls.RequireAndVerifyClientCert
if proto == "tcp" && !authEnabled {
@ -719,7 +717,7 @@ func loadListeners(cli *DaemonCli, serverConfig *apiserver.Config) ([]string, er
return nil, err
}
logrus.Debugf("Listener created for HTTP on %s (%s)", proto, addr)
hosts = append(hosts, protoAddrParts[1])
hosts = append(hosts, addr)
cli.api.Accept(addr, ls...)
}

View file

@ -637,18 +637,18 @@ func parsePortMap(portMap nat.PortMap) ([]*api.PortConfig, error) {
exposedPorts := make([]*api.PortConfig, 0, len(portMap))
for portProtocol, mapping := range portMap {
parts := strings.SplitN(string(portProtocol), "/", 2)
if len(parts) != 2 {
p, proto, ok := strings.Cut(string(portProtocol), "/")
if !ok {
return nil, fmt.Errorf("invalid port mapping: %s", portProtocol)
}
port, err := strconv.ParseUint(parts[0], 10, 16)
port, err := strconv.ParseUint(p, 10, 16)
if err != nil {
return nil, err
}
var protocol api.PortConfig_Protocol
switch strings.ToLower(parts[1]) {
switch strings.ToLower(proto) {
case "tcp":
protocol = api.ProtocolTCP
case "udp":
@ -656,7 +656,7 @@ func parsePortMap(portMap nat.PortMap) ([]*api.PortConfig, error) {
case "sctp":
protocol = api.ProtocolSCTP
default:
return nil, fmt.Errorf("invalid protocol: %s", parts[1])
return nil, fmt.Errorf("invalid protocol: %s", proto)
}
for _, binding := range mapping {

View file

@ -114,11 +114,11 @@ func (e *executor) Describe(ctx context.Context) (*api.NodeDescription, error) {
// parse []string labels into a map[string]string
labels := map[string]string{}
for _, l := range info.Labels {
stringSlice := strings.SplitN(l, "=", 2)
k, v, ok := strings.Cut(l, "=")
// this will take the last value in the list for a given key
// ideally, one shouldn't assign multiple values to the same key
if len(stringSlice) > 1 {
labels[stringSlice[0]] = stringSlice[1]
if ok {
labels[k] = v
}
}

View file

@ -38,16 +38,16 @@ func merge(userConf, imageConf *containertypes.Config) error {
} else {
for _, imageEnv := range imageConf.Env {
found := false
imageEnvKey := strings.Split(imageEnv, "=")[0]
imageEnvKey, _, _ := strings.Cut(imageEnv, "=")
for _, userEnv := range userConf.Env {
userEnvKey := strings.Split(userEnv, "=")[0]
userEnvKey, _, _ := strings.Cut(userEnv, "=")
if isWindows {
// Case insensitive environment variables on Windows
imageEnvKey = strings.ToUpper(imageEnvKey)
userEnvKey = strings.ToUpper(userEnvKey)
found = strings.EqualFold(imageEnvKey, userEnvKey)
} else {
found = imageEnvKey == userEnvKey
}
if imageEnvKey == userEnvKey {
found = true
if found {
break
}
}

View file

@ -107,18 +107,18 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container) ([]lib
if _, err := opts.ValidateExtraHost(extraHost); err != nil {
return nil, err
}
parts := strings.SplitN(extraHost, ":", 2)
host, ip, _ := strings.Cut(extraHost, ":")
// If the IP Address is a string called "host-gateway", replace this
// value with the IP address stored in the daemon level HostGatewayIP
// config variable
if parts[1] == opts.HostGatewayName {
if ip == opts.HostGatewayName {
gateway := daemon.configStore.HostGatewayIP.String()
if gateway == "" {
return nil, fmt.Errorf("unable to derive the IP value for host-gateway")
}
parts[1] = gateway
ip = gateway
}
sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(parts[0], parts[1]))
sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(host, ip))
}
if container.HostConfig.PortBindings != nil {

View file

@ -215,26 +215,27 @@ func parseSecurityOpt(container *container.Container, config *containertypes.Hos
continue
}
var con []string
var k, v string
var ok bool
if strings.Contains(opt, "=") {
con = strings.SplitN(opt, "=", 2)
k, v, ok = strings.Cut(opt, "=")
} else if strings.Contains(opt, ":") {
con = strings.SplitN(opt, ":", 2)
k, v, ok = strings.Cut(opt, ":")
logrus.Warn("Security options with `:` as a separator are deprecated and will be completely unsupported in 17.04, use `=` instead.")
}
if len(con) != 2 {
if !ok {
return fmt.Errorf("invalid --security-opt 1: %q", opt)
}
switch con[0] {
switch k {
case "label":
labelOpts = append(labelOpts, con[1])
labelOpts = append(labelOpts, v)
case "apparmor":
container.AppArmorProfile = con[1]
container.AppArmorProfile = v
case "seccomp":
container.SeccompProfile = con[1]
container.SeccompProfile = v
case "no-new-privileges":
noNewPrivileges, err := strconv.ParseBool(con[1])
noNewPrivileges, err := strconv.ParseBool(v)
if err != nil {
return fmt.Errorf("invalid --security-opt 2: %q", opt)
}
@ -1360,8 +1361,8 @@ func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *
return errors.Wrapf(err, "could not get container for %s", name)
}
for child.HostConfig.NetworkMode.IsContainer() {
parts := strings.SplitN(string(child.HostConfig.NetworkMode), ":", 2)
child, err = daemon.GetContainer(parts[1])
cid := child.HostConfig.NetworkMode.ConnectedContainer()
child, err = daemon.GetContainer(cid)
if err != nil {
if errdefs.IsNotFound(err) {
// Trying to link to a non-existing container is not valid, and
@ -1370,7 +1371,7 @@ func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *
// image could not be found (see moby/moby#39823)
err = errdefs.InvalidParameter(err)
}
return errors.Wrapf(err, "Could not get container for %s", parts[1])
return errors.Wrapf(err, "could not get container for %s", cid)
}
}
if child.HostConfig.NetworkMode.IsHost() {

View file

@ -57,9 +57,9 @@ func Scan(text string) (*events.Message, error) {
}
attrs := make(map[string]string)
for _, a := range strings.SplitN(md["attributes"], ", ", -1) {
kv := strings.SplitN(a, "=", 2)
attrs[kv[0]] = kv[1]
for _, a := range strings.Split(md["attributes"], ", ") {
k, v, _ := strings.Cut(a, "=")
attrs[k] = v
}
return &events.Message{

View file

@ -110,15 +110,16 @@ func InitFilter(home string, options []string, _ idtools.IdentityMapping) (graph
return nil, fmt.Errorf("windowsfilter failed to create '%s': %v", home, err)
}
storageOpt := make(map[string]string)
storageOpt["size"] = defaultSandboxSize
for _, v := range options {
opt := strings.SplitN(v, "=", 2)
storageOpt[strings.ToLower(opt[0])] = opt[1]
storageOpt := map[string]string{
"size": defaultSandboxSize,
}
storageOptions, err := parseStorageOpt(storageOpt)
for _, o := range options {
k, v, _ := strings.Cut(o, "=")
storageOpt[strings.ToLower(k)] = v
}
opts, err := parseStorageOpt(storageOpt)
if err != nil {
return nil, fmt.Errorf("windowsfilter failed to parse default storage options - %s", err)
}
@ -130,7 +131,7 @@ func InitFilter(home string, options []string, _ idtools.IdentityMapping) (graph
},
cache: make(map[string]string),
ctr: graphdriver.NewRefCounter(&checker{}),
defaultStorageOpts: storageOptions,
defaultStorageOpts: opts,
}
return d, nil
}

View file

@ -1,7 +1,6 @@
package zfs // import "github.com/docker/docker/daemon/graphdriver/zfs"
import (
"fmt"
"strings"
"github.com/docker/docker/daemon/graphdriver"
@ -12,7 +11,7 @@ import (
func checkRootdirFs(rootdir string) error {
var buf unix.Statfs_t
if err := unix.Statfs(rootdir, &buf); err != nil {
return fmt.Errorf("Failed to access '%s': %s", rootdir, err)
return err
}
// on FreeBSD buf.Fstypename contains ['z', 'f', 's', 0 ... ]
@ -24,15 +23,14 @@ func checkRootdirFs(rootdir string) error {
return nil
}
const maxlen = 12
func getMountpoint(id string) string {
maxlen := 12
// we need to preserve filesystem suffix
suffix := strings.SplitN(id, "-", 2)
if len(suffix) > 1 {
return id[:maxlen] + "-" + suffix[1]
id, suffix, _ := strings.Cut(id, "-")
id = id[:maxlen]
if suffix != "" {
// preserve filesystem suffix.
id += "-" + suffix
}
return id[:maxlen]
return id
}

View file

@ -206,9 +206,7 @@ func (daemon *Daemon) fillAPIInfo(v *types.Info) {
cfg := daemon.configStore
for _, host := range cfg.Hosts {
// cnf.Hosts is normalized during startup, so should always have a scheme/proto
h := strings.SplitN(host, "://", 2)
proto := h[0]
addr := h[1]
proto, addr, _ := strings.Cut(host, "://")
if proto != "tcp" {
continue
}

View file

@ -94,15 +94,15 @@ func (l *Link) ToEnv() []string {
if l.ChildEnvironment != nil {
for _, v := range l.ChildEnvironment {
parts := strings.SplitN(v, "=", 2)
if len(parts) < 2 {
name, val, ok := strings.Cut(v, "=")
if !ok {
continue
}
// Ignore a few variables that are added during docker build (and not really relevant to linked containers)
if parts[0] == "HOME" || parts[0] == "PATH" {
if name == "HOME" || name == "PATH" {
continue
}
env = append(env, fmt.Sprintf("%s_ENV_%s=%s", alias, parts[0], parts[1]))
env = append(env, fmt.Sprintf("%s_ENV_%s=%s", alias, name, val))
}
}
return env

View file

@ -234,8 +234,8 @@ func (j *Journal) Data() (map[string]string, error) {
return m, fmt.Errorf("journald: error enumerating entry data: %w", syscall.Errno(-rc))
}
kv := strings.SplitN(C.GoStringN((*C.char)(data), C.int(len)), "=", 2)
m[kv[0]] = kv[1]
k, v, _ := strings.Cut(C.GoStringN((*C.char)(data), C.int(len)), "=")
m[k] = v
}
}

View file

@ -59,8 +59,8 @@ func (info *Info) ExtraAttributes(keyMod func(string) string) (map[string]string
envMapping := make(map[string]string)
for _, e := range info.ContainerEnv {
if kv := strings.SplitN(e, "=", 2); len(kv) == 2 {
envMapping[kv[0]] = kv[1]
if k, v, ok := strings.Cut(e, "="); ok {
envMapping[k] = v
}
}

View file

@ -241,8 +241,7 @@ func WithNamespaces(daemon *Daemon, c *container.Container) coci.SpecOpts {
// network
if !c.Config.NetworkDisabled {
ns := specs.LinuxNamespace{Type: "network"}
parts := strings.SplitN(string(c.HostConfig.NetworkMode), ":", 2)
if parts[0] == "container" {
if c.HostConfig.NetworkMode.IsContainer() {
nc, err := daemon.getNetworkedContainer(c.ID, c.HostConfig.NetworkMode.ConnectedContainer())
if err != nil {
return err

View file

@ -279,29 +279,31 @@ func (daemon *Daemon) setWindowsCredentialSpec(c *container.Container, s *specs.
// this doesn't seem like a great idea?
credentialSpec := ""
// TODO(thaJeztah): extract validating and parsing SecurityOpt to a reusable function.
for _, secOpt := range c.HostConfig.SecurityOpt {
optSplits := strings.SplitN(secOpt, "=", 2)
if len(optSplits) != 2 {
k, v, ok := strings.Cut(secOpt, "=")
if !ok {
return errdefs.InvalidParameter(fmt.Errorf("invalid security option: no equals sign in supplied value %s", secOpt))
}
if !strings.EqualFold(optSplits[0], "credentialspec") {
return errdefs.InvalidParameter(fmt.Errorf("security option not supported: %s", optSplits[0]))
// FIXME(thaJeztah): options should not be case-insensitive
if !strings.EqualFold(k, "credentialspec") {
return errdefs.InvalidParameter(fmt.Errorf("security option not supported: %s", k))
}
credSpecSplits := strings.SplitN(optSplits[1], "://", 2)
if len(credSpecSplits) != 2 || credSpecSplits[1] == "" {
scheme, value, ok := strings.Cut(v, "://")
if !ok || value == "" {
return errInvalidCredentialSpecSecOpt
}
value := credSpecSplits[1]
var err error
switch strings.ToLower(credSpecSplits[0]) {
switch strings.ToLower(scheme) {
case "file":
if credentialSpec, err = readCredentialSpecFile(c.ID, daemon.root, filepath.Clean(value)); err != nil {
credentialSpec, err = readCredentialSpecFile(c.ID, daemon.root, filepath.Clean(value))
if err != nil {
return errdefs.InvalidParameter(err)
}
case "registry":
if credentialSpec, err = readCredentialSpecRegistry(c.ID, value); err != nil {
credentialSpec, err = readCredentialSpecRegistry(c.ID, value)
if err != nil {
return errdefs.InvalidParameter(err)
}
case "config":
@ -439,44 +441,41 @@ func readCredentialSpecRegistry(id, name string) (string, error) {
// This allows for staging on machines which do not have the necessary components.
func readCredentialSpecFile(id, root, location string) (string, error) {
if filepath.IsAbs(location) {
return "", fmt.Errorf("invalid credential spec - file:// path cannot be absolute")
return "", fmt.Errorf("invalid credential spec: file:// path cannot be absolute")
}
base := filepath.Join(root, credentialSpecFileLocation)
full := filepath.Join(base, location)
if !strings.HasPrefix(full, base) {
return "", fmt.Errorf("invalid credential spec - file:// path must be under %s", base)
return "", fmt.Errorf("invalid credential spec: file:// path must be under %s", base)
}
bcontents, err := os.ReadFile(full)
if err != nil {
return "", errors.Wrapf(err, "credential spec for container %s could not be read from file %q", id, full)
return "", errors.Wrapf(err, "failed to load credential spec for container %s", id)
}
return string(bcontents[:]), nil
}
func setupWindowsDevices(devices []containertypes.DeviceMapping) (specDevices []specs.WindowsDevice, err error) {
if len(devices) == 0 {
return
}
for _, deviceMapping := range devices {
devicePath := deviceMapping.PathOnHost
if strings.HasPrefix(devicePath, "class/") {
devicePath = strings.Replace(devicePath, "class/", "class://", 1)
if strings.HasPrefix(deviceMapping.PathOnHost, "class/") {
specDevices = append(specDevices, specs.WindowsDevice{
ID: strings.TrimPrefix(deviceMapping.PathOnHost, "class/"),
IDType: "class",
})
} else {
idType, id, ok := strings.Cut(deviceMapping.PathOnHost, "://")
if !ok {
return nil, errors.Errorf("invalid device assignment path: '%s', must be 'class/ID' or 'IDType://ID'", deviceMapping.PathOnHost)
}
if idType == "" {
return nil, errors.Errorf("invalid device assignment path: '%s', IDType cannot be empty", deviceMapping.PathOnHost)
}
specDevices = append(specDevices, specs.WindowsDevice{
ID: id,
IDType: idType,
})
}
srcParts := strings.SplitN(devicePath, "://", 2)
if len(srcParts) != 2 {
return nil, errors.Errorf("invalid device assignment path: '%s', must be 'class/ID' or 'IDType://ID'", deviceMapping.PathOnHost)
}
if srcParts[0] == "" {
return nil, errors.Errorf("invalid device assignment path: '%s', IDType cannot be empty", deviceMapping.PathOnHost)
}
wd := specs.WindowsDevice{
ID: srcParts[1],
IDType: srcParts[0],
}
specDevices = append(specDevices, wd)
}
return
return specDevices, nil
}

View file

@ -7,6 +7,7 @@ import (
"strings"
"testing"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/fs"
containertypes "github.com/docker/docker/api/types/container"
@ -87,7 +88,7 @@ func TestSetWindowsCredentialSpecInSpec(t *testing.T) {
spec := &specs.Spec{}
err := daemon.setWindowsCredentialSpec(containerFactory(`file://C:\path\to\my\credspec.json`), spec)
assert.ErrorContains(t, err, "invalid credential spec - file:// path cannot be absolute")
assert.ErrorContains(t, err, "invalid credential spec: file:// path cannot be absolute")
assert.Check(t, spec.Windows == nil)
})
@ -96,7 +97,7 @@ func TestSetWindowsCredentialSpecInSpec(t *testing.T) {
spec := &specs.Spec{}
err := daemon.setWindowsCredentialSpec(containerFactory(`file://..\credspec.json`), spec)
assert.ErrorContains(t, err, fmt.Sprintf("invalid credential spec - file:// path must be under %s", credSpecsDir))
assert.ErrorContains(t, err, fmt.Sprintf("invalid credential spec: file:// path must be under %s", credSpecsDir))
assert.Check(t, spec.Windows == nil)
})
@ -105,9 +106,8 @@ func TestSetWindowsCredentialSpecInSpec(t *testing.T) {
spec := &specs.Spec{}
err := daemon.setWindowsCredentialSpec(containerFactory("file://i-dont-exist.json"), spec)
assert.ErrorContains(t, err, fmt.Sprintf("credential spec for container %s could not be read from file", dummyContainerID))
assert.ErrorContains(t, err, "The system cannot find")
assert.Check(t, is.ErrorContains(err, fmt.Sprintf("failed to load credential spec for container %s", dummyContainerID)))
assert.Check(t, is.ErrorIs(err, os.ErrNotExist))
assert.Check(t, spec.Windows == nil)
})

View file

@ -211,21 +211,21 @@ func (s *DockerCLIBuildSuite) TestBuildEnvironmentReplacementEnv(c *testing.T) {
envCount := 0
for _, env := range envResult {
parts := strings.SplitN(env, "=", 2)
if parts[0] == "bar" {
k, v, _ := strings.Cut(env, "=")
if k == "bar" {
found = true
if parts[1] != "zzz" {
c.Fatalf("Could not find replaced var for env `bar`: got %q instead of `zzz`", parts[1])
if v != "zzz" {
c.Fatalf("Could not find replaced var for env `bar`: got %q instead of `zzz`", v)
}
} else if strings.HasPrefix(parts[0], "env") {
} else if strings.HasPrefix(k, "env") {
envCount++
if parts[1] != "zzz" {
c.Fatalf("%s should be 'zzz' but instead its %q", parts[0], parts[1])
if v != "zzz" {
c.Fatalf("%s should be 'zzz' but instead its %q", k, v)
}
} else if strings.HasPrefix(parts[0], "env") {
} else if strings.HasPrefix(k, "env") {
envCount++
if parts[1] != "foo" {
c.Fatalf("%s should be 'foo' but instead its %q", parts[0], parts[1])
if v != "foo" {
c.Fatalf("%s should be 'foo' but instead its %q", k, v)
}
}
}

View file

@ -10,7 +10,7 @@ import (
"github.com/docker/docker/runconfig"
"gotest.tools/v3/assert"
"gotest.tools/v3/assert/cmp"
is "gotest.tools/v3/assert/cmp"
)
type DockerCLILinksSuite struct {
@ -39,11 +39,8 @@ func (s *DockerCLILinksSuite) TestLinksInvalidContainerTarget(c *testing.T) {
out, _, err := dockerCmdWithError("run", "--link", "bogus:alias", "busybox", "true")
// an invalid container target should produce an error
assert.Assert(c, err != nil, "out: %s", out)
// an invalid container target should produce an error
// note: convert the output to lowercase first as the error string
// capitalization was changed after API version 1.32
assert.Assert(c, strings.Contains(strings.ToLower(out), "could not get container"))
assert.Check(c, is.ErrorContains(err, "could not get container for bogus"))
assert.Check(c, is.Contains(out, "could not get container"))
}
func (s *DockerCLILinksSuite) TestLinksPingLinkedContainers(c *testing.T) {
@ -163,7 +160,7 @@ func (s *DockerCLILinksSuite) TestLinksHostsFilesInject(c *testing.T) {
readContainerFileWithExec(c, idOne, "/etc/hosts")
contentTwo := readContainerFileWithExec(c, idTwo, "/etc/hosts")
// Host is not present in updated hosts file
assert.Assert(c, strings.Contains(string(contentTwo), "onetwo"))
assert.Assert(c, is.Contains(string(contentTwo), "onetwo"))
}
func (s *DockerCLILinksSuite) TestLinksUpdateOnRestart(c *testing.T) {
@ -183,29 +180,29 @@ func (s *DockerCLILinksSuite) TestLinksUpdateOnRestart(c *testing.T) {
return string(matches[1])
}
ip := getIP(content, "one")
assert.Equal(c, ip, realIP)
assert.Check(c, is.Equal(ip, realIP))
ip = getIP(content, "onetwo")
assert.Equal(c, ip, realIP)
assert.Check(c, is.Equal(ip, realIP))
dockerCmd(c, "restart", "one")
realIP = inspectField(c, "one", "NetworkSettings.Networks.bridge.IPAddress")
content = readContainerFileWithExec(c, id, "/etc/hosts")
ip = getIP(content, "one")
assert.Equal(c, ip, realIP)
assert.Check(c, is.Equal(ip, realIP))
ip = getIP(content, "onetwo")
assert.Equal(c, ip, realIP)
assert.Check(c, is.Equal(ip, realIP))
}
func (s *DockerCLILinksSuite) TestLinksEnvs(c *testing.T) {
testRequires(c, DaemonIsLinux)
dockerCmd(c, "run", "-d", "-e", "e1=", "-e", "e2=v2", "-e", "e3=v3=v3", "--name=first", "busybox", "top")
out, _ := dockerCmd(c, "run", "--name=second", "--link=first:first", "busybox", "env")
assert.Assert(c, strings.Contains(out, "FIRST_ENV_e1=\n"))
assert.Assert(c, strings.Contains(out, "FIRST_ENV_e2=v2"))
assert.Assert(c, strings.Contains(out, "FIRST_ENV_e3=v3=v3"))
assert.Assert(c, is.Contains(out, "FIRST_ENV_e1=\n"))
assert.Assert(c, is.Contains(out, "FIRST_ENV_e2=v2"))
assert.Assert(c, is.Contains(out, "FIRST_ENV_e3=v3=v3"))
}
func (s *DockerCLILinksSuite) TestLinkShortDefinition(c *testing.T) {
@ -230,16 +227,16 @@ func (s *DockerCLILinksSuite) TestLinksNetworkHostContainer(c *testing.T) {
out, _, err := dockerCmdWithError("run", "--name", "should_fail", "--link", "host_container:tester", "busybox", "true")
// Running container linking to a container with --net host should have failed
assert.Assert(c, err != nil, "out: %s", out)
assert.Check(c, err != nil, "out: %s", out)
// Running container linking to a container with --net host should have failed
assert.Assert(c, strings.Contains(out, runconfig.ErrConflictHostNetworkAndLinks.Error()))
assert.Check(c, is.Contains(out, runconfig.ErrConflictHostNetworkAndLinks.Error()))
}
func (s *DockerCLILinksSuite) TestLinksEtcHostsRegularFile(c *testing.T) {
testRequires(c, DaemonIsLinux, NotUserNamespace)
out, _ := dockerCmd(c, "run", "--net=host", "busybox", "ls", "-la", "/etc/hosts")
// /etc/hosts should be a regular file
assert.Assert(c, cmp.Regexp("^-.+\n$", out))
assert.Assert(c, is.Regexp("^-.+\n$", out))
}
func (s *DockerCLILinksSuite) TestLinksMultipleWithSameName(c *testing.T) {

View file

@ -1,7 +1,6 @@
package container
import (
"fmt"
"strings"
containertypes "github.com/docker/docker/api/types/container"
@ -91,23 +90,20 @@ func WithVolume(target string) func(*TestContainerConfig) {
// WithBind sets the bind mount of the container
func WithBind(src, target string) func(*TestContainerConfig) {
return func(c *TestContainerConfig) {
c.HostConfig.Binds = append(c.HostConfig.Binds, fmt.Sprintf("%s:%s", src, target))
c.HostConfig.Binds = append(c.HostConfig.Binds, src+":"+target)
}
}
// WithTmpfs sets a target path in the container to a tmpfs
func WithTmpfs(target string) func(config *TestContainerConfig) {
// WithTmpfs sets a target path in the container to a tmpfs, with optional options
// (separated with a colon).
func WithTmpfs(targetAndOpts string) func(config *TestContainerConfig) {
return func(c *TestContainerConfig) {
if c.HostConfig.Tmpfs == nil {
c.HostConfig.Tmpfs = make(map[string]string)
}
spec := strings.SplitN(target, ":", 2)
var opts string
if len(spec) > 1 {
opts = spec[1]
}
c.HostConfig.Tmpfs[spec[0]] = opts
target, opts, _ := strings.Cut(targetAndOpts, ":")
c.HostConfig.Tmpfs[target] = opts
}
}

View file

@ -7,9 +7,8 @@ import "strings"
func setupEnvironmentVariables(a []string) map[string]string {
r := make(map[string]string)
for _, s := range a {
arr := strings.SplitN(s, "=", 2)
if len(arr) == 2 {
r[arr[0]] = arr[1]
if k, v, ok := strings.Cut(s, "="); ok {
r[k] = v
}
}
return r

View file

@ -222,12 +222,12 @@ func newClient(scope string, kv string, addr string, config *store.Config, cache
}
}
store, err := libkv.NewStore(store.Backend(kv), addrs, config)
s, err := libkv.NewStore(store.Backend(kv), addrs, config)
if err != nil {
return nil, err
}
ds := &datastore{scope: scope, store: store, active: true, watchCh: make(chan struct{}), sequential: sequential}
ds := &datastore{scope: scope, store: s, active: true, watchCh: make(chan struct{}), sequential: sequential}
if cached {
ds.cache = newCache(ds)
}

View file

@ -75,20 +75,19 @@ func Build(path, IP, hostname, domainname string, extraContent []Record) error {
content := bytes.NewBuffer(nil)
if IP != "" {
//set main record
// set main record
var mainRec Record
mainRec.IP = IP
// User might have provided a FQDN in hostname or split it across hostname
// and domainname. We want the FQDN and the bare hostname.
fqdn := hostname
if domainname != "" {
fqdn = fmt.Sprintf("%s.%s", fqdn, domainname)
fqdn += "." + domainname
}
parts := strings.SplitN(fqdn, ".", 2)
if len(parts) == 2 {
mainRec.Hosts = fmt.Sprintf("%s %s", fqdn, parts[0])
} else {
mainRec.Hosts = fqdn
mainRec.Hosts = fqdn
if hostName, _, ok := strings.Cut(fqdn, "."); ok {
mainRec.Hosts += " " + hostName
}
if _, err := mainRec.WriteTo(content); err != nil {
return err

View file

@ -30,7 +30,7 @@ const (
// DNSServers A list of DNS servers associated with the endpoint
DNSServers = Prefix + ".endpoint.dnsservers"
//EnableIPv6 constant represents enabling IPV6 at network level
// EnableIPv6 constant represents enabling IPV6 at network level
EnableIPv6 = Prefix + ".enable_ipv6"
// DriverMTU constant represents the MTU size for the network driver
@ -106,27 +106,18 @@ func MakeKVClient(scope string) string {
// Key extracts the key portion of the label
func Key(label string) (key string) {
if kv := strings.SplitN(label, "=", 2); len(kv) > 0 {
key = kv[0]
}
return
key, _, _ = strings.Cut(label, "=")
return key
}
// Value extracts the value portion of the label
func Value(label string) (value string) {
if kv := strings.SplitN(label, "=", 2); len(kv) > 1 {
value = kv[1]
}
return
_, value, _ = strings.Cut(label, "=")
return value
}
// KeyValue decomposes the label in the (key,value) pair
func KeyValue(label string) (key string, value string) {
if kv := strings.SplitN(label, "=", 2); len(kv) > 0 {
key = kv[0]
if len(kv) > 1 {
value = kv[1]
}
}
return
key, value, _ = strings.Cut(label, "=")
return key, value
}

View file

@ -110,19 +110,18 @@ func (sb *sandbox) updateHostsFile(ifaceIPs []string) error {
// User might have provided a FQDN in hostname or split it across hostname
// and domainname. We want the FQDN and the bare hostname.
fqdn := sb.config.hostName
mhost := sb.config.hostName
if sb.config.domainName != "" {
fqdn = fmt.Sprintf("%s.%s", fqdn, sb.config.domainName)
fqdn += "." + sb.config.domainName
}
hosts := fqdn
parts := strings.SplitN(fqdn, ".", 2)
if len(parts) == 2 {
mhost = fmt.Sprintf("%s %s", fqdn, parts[0])
if hostName, _, ok := strings.Cut(fqdn, "."); ok {
hosts += " " + hostName
}
var extraContent []etchosts.Record
for _, ip := range ifaceIPs {
extraContent = append(extraContent, etchosts.Record{Hosts: mhost, IP: ip})
extraContent = append(extraContent, etchosts.Record{Hosts: hosts, IP: ip})
}
sb.addHostsEntries(extraContent)

View file

@ -31,19 +31,17 @@ func (p *PoolsOpt) Set(value string) error {
poolsDef := types.NetworkToSplit{}
for _, field := range fields {
parts := strings.SplitN(field, "=", 2)
if len(parts) != 2 {
// TODO(thaJeztah): this should not be case-insensitive.
key, val, ok := strings.Cut(strings.ToLower(field), "=")
if !ok {
return fmt.Errorf("invalid field '%s' must be a key=value pair", field)
}
key := strings.ToLower(parts[0])
value := strings.ToLower(parts[1])
switch key {
case "base":
poolsDef.Base = value
poolsDef.Base = val
case "size":
size, err := strconv.Atoi(value)
size, err := strconv.Atoi(val)
if err != nil {
return fmt.Errorf("invalid size value: %q (must be integer): %v", value, err)
}

View file

@ -16,15 +16,15 @@ import (
//
// The only validation here is to check if name is empty, per #25099
func ValidateEnv(val string) (string, error) {
arr := strings.SplitN(val, "=", 2)
if arr[0] == "" {
k, _, ok := strings.Cut(val, "=")
if k == "" {
return "", errors.New("invalid environment variable: " + val)
}
if len(arr) > 1 {
if ok {
return val, nil
}
if envVal, ok := os.LookupEnv(arr[0]); ok {
return arr[0] + "=" + envVal, nil
if envVal, ok := os.LookupEnv(k); ok {
return k + "=" + envVal, nil
}
return val, nil
}

View file

@ -60,8 +60,7 @@ func ParseHost(defaultToTLS, defaultToUnixXDG bool, val string) (string, error)
if err != nil {
return "", err
}
socket := filepath.Join(runtimeDir, "docker.sock")
host = "unix://" + socket
host = "unix://" + filepath.Join(runtimeDir, "docker.sock")
} else {
host = DefaultHost
}
@ -77,23 +76,32 @@ func ParseHost(defaultToTLS, defaultToUnixXDG bool, val string) (string, error)
// parseDaemonHost parses the specified address and returns an address that will be used as the host.
// Depending on the address specified, this may return one of the global Default* strings defined in hosts.go.
func parseDaemonHost(addr string) (string, error) {
addrParts := strings.SplitN(addr, "://", 2)
if len(addrParts) == 1 && addrParts[0] != "" {
addrParts = []string{"tcp", addrParts[0]}
func parseDaemonHost(address string) (string, error) {
proto, addr, ok := strings.Cut(address, "://")
if !ok && proto != "" {
addr = proto
proto = "tcp"
}
switch addrParts[0] {
switch proto {
case "tcp":
return ParseTCPAddr(addr, DefaultTCPHost)
return ParseTCPAddr(address, DefaultTCPHost)
case "unix":
return parseSimpleProtoAddr("unix", addrParts[1], DefaultUnixSocket)
a, err := parseSimpleProtoAddr(proto, addr, DefaultUnixSocket)
if err != nil {
return "", errors.Wrapf(err, "invalid bind address (%s)", address)
}
return a, nil
case "npipe":
return parseSimpleProtoAddr("npipe", addrParts[1], DefaultNamedPipe)
a, err := parseSimpleProtoAddr(proto, addr, DefaultNamedPipe)
if err != nil {
return "", errors.Wrapf(err, "invalid bind address (%s)", address)
}
return a, nil
case "fd":
return addr, nil
return address, nil
default:
return "", errors.Errorf("invalid bind address (%s): unsupported proto '%s'", addr, addrParts[0])
return "", errors.Errorf("invalid bind address (%s): unsupported proto '%s'", address, proto)
}
}
@ -102,9 +110,8 @@ func parseDaemonHost(addr string) (string, error) {
// socket address, either using the address parsed from addr, or the contents of
// defaultAddr if addr is a blank string.
func parseSimpleProtoAddr(proto, addr, defaultAddr string) (string, error) {
addr = strings.TrimPrefix(addr, proto+"://")
if strings.Contains(addr, "://") {
return "", errors.Errorf("invalid proto, expected %s: %s", proto, addr)
return "", errors.Errorf("invalid %s address: %s", proto, addr)
}
if addr == "" {
addr = defaultAddr
@ -172,14 +179,14 @@ func parseTCPAddr(address string, strict bool) (*url.URL, error) {
// ExtraHost is in the form of name:ip where the ip has to be a valid ip (IPv4 or IPv6).
func ValidateExtraHost(val string) (string, error) {
// allow for IPv6 addresses in extra hosts by only splitting on first ":"
arr := strings.SplitN(val, ":", 2)
if len(arr) != 2 || len(arr[0]) == 0 {
name, ip, ok := strings.Cut(val, ":")
if !ok || name == "" {
return "", errors.Errorf("bad format for add-host: %q", val)
}
// Skip IPaddr validation for special "host-gateway" string
if arr[1] != HostGatewayName {
if _, err := ValidateIPAddress(arr[1]); err != nil {
return "", errors.Errorf("invalid IP address in add-host: %q", arr[1])
if ip != HostGatewayName {
if _, err := ValidateIPAddress(ip); err != nil {
return "", errors.Errorf("invalid IP address in add-host: %q", ip)
}
}
return val, nil

View file

@ -85,6 +85,8 @@ func TestParseDockerDaemonHost(t *testing.T) {
"[0:0:0:0:0:0:0:1]:5555/path": "invalid bind address ([0:0:0:0:0:0:0:1]:5555/path): should not contain a path element",
"tcp://:5555/path": "invalid bind address (tcp://:5555/path): should not contain a path element",
"localhost:5555/path": "invalid bind address (localhost:5555/path): should not contain a path element",
"unix://tcp://127.0.0.1": "invalid bind address (unix://tcp://127.0.0.1): invalid unix address: tcp://127.0.0.1",
"unix://unix://tcp://127.0.0.1": "invalid bind address (unix://unix://tcp://127.0.0.1): invalid unix address: unix://tcp://127.0.0.1",
}
valids := map[string]string{
":": DefaultTCPHost,
@ -130,7 +132,7 @@ func TestParseDockerDaemonHost(t *testing.T) {
t.Errorf(`unexpected error: "%v"`, err)
}
if addr != expectedAddr {
t.Errorf(`expected "%s", got "%s""`, expectedAddr, addr)
t.Errorf(`expected "%s", got "%s"`, expectedAddr, addr)
}
})
}
@ -210,18 +212,6 @@ func TestParseTCP(t *testing.T) {
}
}
func TestParseInvalidUnixAddrInvalid(t *testing.T) {
if _, err := parseSimpleProtoAddr("unix", "tcp://127.0.0.1", "unix:///var/run/docker.sock"); err == nil || err.Error() != "invalid proto, expected unix: tcp://127.0.0.1" {
t.Fatalf("Expected an error, got %v", err)
}
if _, err := parseSimpleProtoAddr("unix", "unix://tcp://127.0.0.1", "/var/run/docker.sock"); err == nil || err.Error() != "invalid proto, expected unix: tcp://127.0.0.1" {
t.Fatalf("Expected an error, got %v", err)
}
if v, err := parseSimpleProtoAddr("unix", "", "/var/run/docker.sock"); err != nil || v != "unix:///var/run/docker.sock" {
t.Fatalf("Expected an %v, got %v", v, "unix:///var/run/docker.sock")
}
}
func TestValidateExtraHosts(t *testing.T) {
valid := []string{
`myhost:192.168.0.1`,

View file

@ -162,12 +162,8 @@ func (opts *MapOpts) Set(value string) error {
}
value = v
}
vals := strings.SplitN(value, "=", 2)
if len(vals) == 1 {
(opts.values)[vals[0]] = ""
} else {
(opts.values)[vals[0]] = vals[1]
}
k, v, _ := strings.Cut(value, "=")
(opts.values)[k] = v
return nil
}

View file

@ -29,27 +29,29 @@ func (o *RuntimeOpt) Name() string {
// Set validates and updates the list of Runtimes
func (o *RuntimeOpt) Set(val string) error {
parts := strings.SplitN(val, "=", 2)
if len(parts) != 2 {
k, v, ok := strings.Cut(val, "=")
if !ok {
return fmt.Errorf("invalid runtime argument: %s", val)
}
parts[0] = strings.TrimSpace(parts[0])
parts[1] = strings.TrimSpace(parts[1])
if parts[0] == "" || parts[1] == "" {
// TODO(thaJeztah): this should not accept spaces.
k = strings.TrimSpace(k)
v = strings.TrimSpace(v)
if k == "" || v == "" {
return fmt.Errorf("invalid runtime argument: %s", val)
}
parts[0] = strings.ToLower(parts[0])
if parts[0] == o.stockRuntimeName {
// TODO(thaJeztah): this should not be case-insensitive.
k = strings.ToLower(k)
if k == o.stockRuntimeName {
return fmt.Errorf("runtime name '%s' is reserved", o.stockRuntimeName)
}
if _, ok := (*o.values)[parts[0]]; ok {
return fmt.Errorf("runtime '%s' was already defined", parts[0])
if _, ok := (*o.values)[k]; ok {
return fmt.Errorf("runtime '%s' was already defined", k)
}
(*o.values)[parts[0]] = types.Runtime{Path: parts[1]}
(*o.values)[k] = types.Runtime{Path: v}
return nil
}

View file

@ -26,27 +26,24 @@ func GetKernelVersion() (*VersionInfo, error) {
// getRelease uses `system_profiler SPSoftwareDataType` to get OSX kernel version
func getRelease(osName string) (string, error) {
var release string
data := strings.Split(osName, "\n")
for _, line := range data {
for _, line := range strings.Split(osName, "\n") {
if !strings.Contains(line, "Kernel Version") {
continue
}
// It has the format like ' Kernel Version: Darwin 14.5.0'
content := strings.SplitN(line, ":", 2)
if len(content) != 2 {
return "", fmt.Errorf("Kernel Version is invalid")
_, ver, ok := strings.Cut(line, ":")
if !ok {
return "", fmt.Errorf("kernel Version is invalid")
}
prettyNames := strings.SplitN(strings.TrimSpace(content[1]), " ", 2)
if len(prettyNames) != 2 {
return "", fmt.Errorf("Kernel Version needs to be 'Darwin x.x.x' ")
_, release, ok := strings.Cut(strings.TrimSpace(ver), " ")
if !ok {
return "", fmt.Errorf("kernel version needs to be 'Darwin x.x.x'")
}
release = prettyNames[1]
return release, nil
}
return release, nil
return "", nil
}
func getSPSoftwareDataType() (string, error) {

View file

@ -5,7 +5,6 @@ package operatingsystem // import "github.com/docker/docker/pkg/parsers/operatin
import (
"bufio"
"bytes"
"fmt"
"os"
"strings"
)
@ -44,23 +43,22 @@ func getValueFromOsRelease(key string) (string, error) {
osReleaseFile, err := os.Open(etcOsRelease)
if err != nil {
if !os.IsNotExist(err) {
return "", fmt.Errorf("Error opening %s: %v", etcOsRelease, err)
return "", err
}
osReleaseFile, err = os.Open(altOsRelease)
if err != nil {
return "", fmt.Errorf("Error opening %s: %v", altOsRelease, err)
return "", err
}
}
defer osReleaseFile.Close()
var value string
keyWithTrailingEqual := key + "="
scanner := bufio.NewScanner(osReleaseFile)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, keyWithTrailingEqual) {
data := strings.SplitN(line, "=", 2)
value = strings.Trim(data[1], `"' `) // remove leading/trailing quotes and whitespace
if strings.HasPrefix(line, key+"=") {
value = strings.TrimPrefix(line, key+"=")
value = strings.Trim(value, `"' `) // remove leading/trailing quotes and whitespace
}
}

View file

@ -9,13 +9,14 @@ import (
"strings"
)
// ParseKeyValueOpt parses and validates the specified string as a key/value pair (key=value)
func ParseKeyValueOpt(opt string) (string, string, error) {
parts := strings.SplitN(opt, "=", 2)
if len(parts) != 2 {
return "", "", fmt.Errorf("Unable to parse key/value option: %s", opt)
// ParseKeyValueOpt parses and validates the specified string as a key/value
// pair (key=value).
func ParseKeyValueOpt(opt string) (key string, value string, err error) {
k, v, ok := strings.Cut(opt, "=")
if !ok {
return "", "", fmt.Errorf("unable to parse key/value option: %s", opt)
}
return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil
return strings.TrimSpace(k), strings.TrimSpace(v), nil
}
// ParseUintListMaximum parses and validates the specified string as the value
@ -75,12 +76,12 @@ func parseUintList(val string, maximum int) (map[int]bool, error) {
}
availableInts[v] = true
} else {
split := strings.SplitN(r, "-", 2)
min, err := strconv.Atoi(split[0])
minS, maxS, _ := strings.Cut(r, "-")
min, err := strconv.Atoi(minS)
if err != nil {
return nil, errInvalidFormat
}
max, err := strconv.Atoi(split[1])
max, err := strconv.Atoi(maxS)
if err != nil {
return nil, errInvalidFormat
}

View file

@ -7,8 +7,8 @@ import (
func TestParseKeyValueOpt(t *testing.T) {
invalids := map[string]string{
"": "Unable to parse key/value option: ",
"key": "Unable to parse key/value option: key",
"": "unable to parse key/value option: ",
"key": "unable to parse key/value option: key",
}
for invalid, expectedError := range invalids {
if _, _, err := ParseKeyValueOpt(invalid); err == nil || err.Error() != expectedError {

View file

@ -62,13 +62,11 @@ func NewTarSumHash(r io.Reader, dc bool, v Version, tHash THash) (TarSum, error)
// NewTarSumForLabel creates a new TarSum using the provided TarSum version+hash label.
func NewTarSumForLabel(r io.Reader, disableCompression bool, label string) (TarSum, error) {
parts := strings.SplitN(label, "+", 2)
if len(parts) != 2 {
versionName, hashName, ok := strings.Cut(label, "+")
if !ok {
return nil, errors.New("tarsum label string should be of the form: {tarsum_version}+{hash_name}")
}
versionName, hashName := parts[0], parts[1]
version, ok := tarSumVersionsByName[versionName]
if !ok {
return nil, fmt.Errorf("unknown TarSum version name: %q", versionName)

View file

@ -69,16 +69,12 @@ func (tsv Version) String() string {
// GetVersionFromTarsum returns the Version from the provided string.
func GetVersionFromTarsum(tarsum string) (Version, error) {
tsv := tarsum
if strings.Contains(tarsum, "+") {
tsv = strings.SplitN(tarsum, "+", 2)[0]
versionName, _, _ := strings.Cut(tarsum, "+")
version, ok := tarSumVersionsByName[versionName]
if !ok {
return -1, ErrNotVersion
}
for v, s := range tarSumVersions {
if s == tsv {
return v, nil
}
}
return -1, ErrNotVersion
return version, nil
}
// Errors that may be returned by functions in this package

View file

@ -1,7 +1,6 @@
package plugin // import "github.com/docker/docker/plugin"
import (
"fmt"
"strings"
"sync"
@ -56,15 +55,13 @@ func WithEnv(env []string) CreateOpt {
}
}
for _, line := range env {
if pair := strings.SplitN(line, "=", 2); len(pair) > 1 {
effectiveEnv[pair[0]] = pair[1]
if k, v, ok := strings.Cut(line, "="); ok {
effectiveEnv[k] = v
}
}
p.PluginObj.Settings.Env = make([]string, len(effectiveEnv))
i := 0
p.PluginObj.Settings.Env = make([]string, 0, len(effectiveEnv))
for key, value := range effectiveEnv {
p.PluginObj.Settings.Env[i] = fmt.Sprintf("%s=%s", key, value)
i++
p.PluginObj.Settings.Env = append(p.PluginObj.Settings.Env, key+"="+value)
}
}
}

View file

@ -92,11 +92,11 @@ func (set *settable) isSettable(allowedSettableFields []string, settable []strin
func updateSettingsEnv(env *[]string, set *settable) {
for i, e := range *env {
if parts := strings.SplitN(e, "=", 2); parts[0] == set.name {
(*env)[i] = fmt.Sprintf("%s=%s", set.name, set.value)
if name, _, _ := strings.Cut(e, "="); name == set.name {
(*env)[i] = set.name + "=" + set.value
return
}
}
*env = append(*env, fmt.Sprintf("%s=%s", set.name, set.value))
*env = append(*env, set.name+"="+set.value)
}

View file

@ -8,12 +8,8 @@ import (
func ConvertKVStringsToMap(values []string) map[string]string {
result := make(map[string]string, len(values))
for _, value := range values {
kv := strings.SplitN(value, "=", 2)
if len(kv) == 1 {
result[kv[0]] = ""
} else {
result[kv[0]] = kv[1]
}
k, v, _ := strings.Cut(value, "=")
result[k] = v
}
return result

View file

@ -371,11 +371,9 @@ func (v *localVolume) saveOpts() error {
// getAddress finds out address/hostname from options
func getAddress(opts string) string {
optsList := strings.Split(opts, ",")
for i := 0; i < len(optsList); i++ {
if strings.HasPrefix(optsList[i], "addr=") {
addr := strings.SplitN(optsList[i], "=", 2)[1]
return addr
for _, opt := range strings.Split(opts, ",") {
if strings.HasPrefix(opt, "addr=") {
return strings.TrimPrefix(opt, "addr=")
}
}
return ""
@ -383,11 +381,9 @@ func getAddress(opts string) string {
// getPassword finds out a password from options
func getPassword(opts string) string {
optsList := strings.Split(opts, ",")
for i := 0; i < len(optsList); i++ {
if strings.HasPrefix(optsList[i], "password=") {
passwd := strings.SplitN(optsList[i], "=", 2)[1]
return passwd
for _, opt := range strings.Split(opts, ",") {
if strings.HasPrefix(opt, "password=") {
return strings.TrimPrefix(opt, "password=")
}
}
return ""

View file

@ -23,18 +23,6 @@ type linuxParser struct {
fi fileInfoProvider
}
func linuxSplitRawSpec(raw string) ([]string, error) {
if strings.Count(raw, ":") > 2 {
return nil, errInvalidSpec(raw)
}
arr := strings.SplitN(raw, ":", 3)
if arr[0] == "" {
return nil, errInvalidSpec(raw)
}
return arr, nil
}
func linuxValidateNotRoot(p string) error {
p = path.Clean(strings.ReplaceAll(p, `\`, `/`))
if p == "/" {
@ -214,9 +202,9 @@ func (p *linuxParser) ReadWrite(mode string) bool {
}
func (p *linuxParser) ParseMountRaw(raw, volumeDriver string) (*MountPoint, error) {
arr, err := linuxSplitRawSpec(raw)
if err != nil {
return nil, err
arr := strings.SplitN(raw, ":", 4)
if arr[0] == "" {
return nil, errInvalidSpec(raw)
}
var spec mount.Mount
@ -334,26 +322,23 @@ func (p *linuxParser) ParseVolumesFrom(spec string) (string, string, error) {
return "", "", fmt.Errorf("volumes-from specification cannot be an empty string")
}
specParts := strings.SplitN(spec, ":", 2)
id := specParts[0]
mode := "rw"
if len(specParts) == 2 {
mode = specParts[1]
if !linuxValidMountMode(mode) {
return "", "", errInvalidMode(mode)
}
// For now don't allow propagation properties while importing
// volumes from data container. These volumes will inherit
// the same propagation property as of the original volume
// in data container. This probably can be relaxed in future.
if linuxHasPropagation(mode) {
return "", "", errInvalidMode(mode)
}
// Do not allow copy modes on volumes-from
if _, isSet := getCopyMode(mode, p.DefaultCopyMode()); isSet {
return "", "", errInvalidMode(mode)
}
id, mode, _ := strings.Cut(spec, ":")
if mode == "" {
return id, "rw", nil
}
if !linuxValidMountMode(mode) {
return "", "", errInvalidMode(mode)
}
// For now don't allow propagation properties while importing
// volumes from data container. These volumes will inherit
// the same propagation property as of the original volume
// in data container. This probably can be relaxed in future.
if linuxHasPropagation(mode) {
return "", "", errInvalidMode(mode)
}
// Do not allow copy modes on volumes-from
if _, isSet := getCopyMode(mode, p.DefaultCopyMode()); isSet {
return "", "", errInvalidMode(mode)
}
return id, mode, nil
}

View file

@ -415,20 +415,18 @@ func (p *windowsParser) ParseVolumesFrom(spec string) (string, string, error) {
return "", "", fmt.Errorf("volumes-from specification cannot be an empty string")
}
specParts := strings.SplitN(spec, ":", 2)
id := specParts[0]
mode := "rw"
id, mode, _ := strings.Cut(spec, ":")
if mode == "" {
return id, "rw", nil
}
if len(specParts) == 2 {
mode = specParts[1]
if !windowsValidMountMode(mode) {
return "", "", errInvalidMode(mode)
}
if !windowsValidMountMode(mode) {
return "", "", errInvalidMode(mode)
}
// Do not allow copy modes on volumes-from
if _, isSet := getCopyMode(mode, p.DefaultCopyMode()); isSet {
return "", "", errInvalidMode(mode)
}
// Do not allow copy modes on volumes-from
if _, isSet := getCopyMode(mode, p.DefaultCopyMode()); isSet {
return "", "", errInvalidMode(mode)
}
return id, mode, nil
}