docker/swarmkit

View on GitHub
swarmd/cmd/swarmctl/service/flagparser/tmpfs.go

Summary

Maintainability
C
1 day
Test Coverage
package flagparser

import (
    "os"
    "path"
    "strconv"
    "strings"

    "github.com/moby/swarmkit/v2/api"
    "github.com/pkg/errors"
    "github.com/spf13/pflag"
)

// parseTmpfs supports a simple tmpfs decl, similar to docker run.
//
// This should go away.
func parseTmpfs(flags *pflag.FlagSet, spec *api.ServiceSpec) error {
    if flags.Changed("tmpfs") {
        tmpfss, err := flags.GetStringSlice("tmpfs")
        if err != nil {
            return err
        }

        container := spec.Task.GetContainer()
        // TODO(stevvooe): Nasty inline parsing code, replace with mount syntax.
        for _, tmpfs := range tmpfss {
            parts := strings.SplitN(tmpfs, ":", 2)

            if len(parts) < 1 {
                return errors.Errorf("invalid mount spec: %v", tmpfs)
            }

            if len(parts[0]) == 0 || !path.IsAbs(parts[0]) {
                return errors.Errorf("invalid mount spec: %v", tmpfs)
            }

            m := api.Mount{
                Type:   api.MountTypeTmpfs,
                Target: parts[0],
            }

            if len(parts) == 2 {
                if strings.Contains(parts[1], ":") {
                    // repeated colon is illegal
                    return errors.Errorf("invalid mount spec: %v", tmpfs)
                }

                // BUG(stevvooe): Cobra stringslice actually doesn't correctly
                // handle comma separated values, so multiple flags aren't
                // really supported. We'll have to replace StringSlice with a
                // type that doesn't use the csv parser. This is good enough
                // for now.

                flags := strings.Split(parts[1], ",")
                var opts api.Mount_TmpfsOptions
                for _, flag := range flags {
                    switch {
                    case strings.HasPrefix(flag, "size="):
                        meat := strings.TrimPrefix(flag, "size=")

                        // try to parse this into bytes
                        i, err := strconv.ParseInt(meat, 10, 64)
                        if err != nil {
                            // remove suffix and try again
                            suffix := meat[len(meat)-1]
                            meat = meat[:len(meat)-1]
                            var multiplier int64
                            switch suffix {
                            case 'g':
                                multiplier = 1 << 30
                            case 'm':
                                multiplier = 1 << 20
                            case 'k':
                                multiplier = 1 << 10
                            default:
                                return errors.Errorf("invalid size format: %v", flag)
                            }

                            // reparse the meat
                            var err error
                            i, err = strconv.ParseInt(meat, 10, 64)
                            if err != nil {
                                return err
                            }

                            i *= multiplier
                        }
                        opts.SizeBytes = i
                    case strings.HasPrefix(flag, "mode="):
                        meat := strings.TrimPrefix(flag, "mode=")
                        i, err := strconv.ParseInt(meat, 8, 32)
                        if err != nil {
                            return err
                        }
                        opts.Mode = os.FileMode(i)
                    case flag == "ro":
                        m.ReadOnly = true
                    case flag == "rw":
                        m.ReadOnly = false
                    case flag == "exec":
                        opts.Options = "exec"
                    case flag == "noexec":
                        opts.Options = "noexec"
                    default:
                        return errors.New("unsupported flag")
                    }
                }
                m.TmpfsOptions = &opts
            }

            container.Mounts = append(container.Mounts, m)
        }
    }

    return nil
}