diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/adapter/pods.go | 22 | ||||
-rw-r--r-- | pkg/api/handlers/generic/containers_stats.go | 15 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/containers.go | 17 | ||||
-rw-r--r-- | pkg/api/server/register_swagger.go | 26 | ||||
-rw-r--r-- | pkg/api/server/server.go | 1 | ||||
-rw-r--r-- | pkg/bindings/connection.go | 12 | ||||
-rw-r--r-- | pkg/bindings/containers/containers.go | 17 | ||||
-rw-r--r-- | pkg/bindings/images/images.go | 4 | ||||
-rw-r--r-- | pkg/bindings/images/search.go | 2 | ||||
-rw-r--r-- | pkg/bindings/pods/pods.go | 2 | ||||
-rw-r--r-- | pkg/cgroups/cgroups.go | 15 | ||||
-rw-r--r-- | pkg/cgroups/cpu.go | 39 | ||||
-rw-r--r-- | pkg/rootlessport/rootlessport_linux.go | 29 | ||||
-rw-r--r-- | pkg/spec/config_linux.go | 37 | ||||
-rw-r--r-- | pkg/spec/config_unsupported.go | 4 | ||||
-rw-r--r-- | pkg/spec/createconfig.go | 1 | ||||
-rw-r--r-- | pkg/spec/parse.go | 18 | ||||
-rw-r--r-- | pkg/spec/spec.go | 6 |
18 files changed, 222 insertions, 45 deletions
diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go index b0e63f770..a30ec6649 100644 --- a/pkg/adapter/pods.go +++ b/pkg/adapter/pods.go @@ -764,7 +764,6 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container containerConfig.ImageID = newImage.ID() containerConfig.Name = containerYAML.Name containerConfig.Tty = containerYAML.TTY - containerConfig.WorkDir = containerYAML.WorkingDir containerConfig.Pod = podID @@ -796,6 +795,27 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container containerConfig.StopSignal = 15 + containerConfig.WorkDir = "/" + if imageData != nil { + // FIXME, + // we are currently ignoring imageData.Config.ExposedPorts + containerConfig.BuiltinImgVolumes = imageData.Config.Volumes + if imageData.Config.WorkingDir != "" { + containerConfig.WorkDir = imageData.Config.WorkingDir + } + containerConfig.Labels = imageData.Config.Labels + if imageData.Config.StopSignal != "" { + stopSignal, err := util.ParseSignal(imageData.Config.StopSignal) + if err != nil { + return nil, err + } + containerConfig.StopSignal = stopSignal + } + } + + if containerYAML.WorkingDir != "" { + containerConfig.WorkDir = containerYAML.WorkingDir + } // If the user does not pass in ID mappings, just set to basics if userConfig.IDMappings == nil { userConfig.IDMappings = &storage.IDMappingOptions{} diff --git a/pkg/api/handlers/generic/containers_stats.go b/pkg/api/handlers/generic/containers_stats.go index cbc1be2f0..19e2cc882 100644 --- a/pkg/api/handlers/generic/containers_stats.go +++ b/pkg/api/handlers/generic/containers_stats.go @@ -61,14 +61,15 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { var preCPUStats docker.CPUStats if query.Stream { preRead = time.Now() + systemUsage, _ := cgroups.GetSystemCPUUsage() preCPUStats = docker.CPUStats{ CPUUsage: docker.CPUUsage{ TotalUsage: stats.CPUNano, - PercpuUsage: []uint64{uint64(stats.CPU)}, - UsageInKernelmode: 0, - UsageInUsermode: 0, + PercpuUsage: stats.PerCPU, + UsageInKernelmode: stats.CPUSystemNano, + UsageInUsermode: stats.CPUNano - stats.CPUSystemNano, }, - SystemUsage: 0, + SystemUsage: systemUsage, OnlineCPUs: 0, ThrottlingData: docker.ThrottlingData{}, } @@ -122,6 +123,8 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { InstanceID: "", } + systemUsage, _ := cgroups.GetSystemCPUUsage() + s := handlers.Stats{StatsJSON: docker.StatsJSON{ Stats: docker.Stats{ Read: time.Now(), @@ -143,11 +146,11 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { CPUStats: docker.CPUStats{ CPUUsage: docker.CPUUsage{ TotalUsage: cgroupStat.CPU.Usage.Total, - PercpuUsage: []uint64{uint64(stats.CPU)}, + PercpuUsage: cgroupStat.CPU.Usage.PerCPU, UsageInKernelmode: cgroupStat.CPU.Usage.Kernel, UsageInUsermode: cgroupStat.CPU.Usage.Total - cgroupStat.CPU.Usage.Kernel, }, - SystemUsage: 0, + SystemUsage: systemUsage, OnlineCPUs: uint32(len(cgroupStat.CPU.Usage.PerCPU)), ThrottlingData: docker.ThrottlingData{ Periods: 0, diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go index a64ed446c..ce2836a72 100644 --- a/pkg/api/handlers/libpod/containers.go +++ b/pkg/api/handlers/libpod/containers.go @@ -56,7 +56,7 @@ func ListContainers(w http.ResponseWriter, r *http.Request) { decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { All bool `schema:"all"` - Filter map[string][]string `schema:"filter"` + Filters map[string][]string `schema:"filters"` Last int `schema:"last"` Namespace bool `schema:"namespace"` Pod bool `schema:"pod"` @@ -71,6 +71,7 @@ func ListContainers(w http.ResponseWriter, r *http.Request) { errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String())) return } + runtime := r.Context().Value("runtime").(*libpod.Runtime) opts := shared.PsOptions{ All: query.All, @@ -82,8 +83,10 @@ func ListContainers(w http.ResponseWriter, r *http.Request) { Pod: query.Pod, Sync: query.Sync, } - if len(query.Filter) > 0 { - for k, v := range query.Filter { + + all := query.All + if len(query.Filters) > 0 { + for k, v := range query.Filters { for _, val := range v { generatedFunc, err := shared.GenerateContainerFilterFuncs(k, val, runtime) if err != nil { @@ -95,8 +98,12 @@ func ListContainers(w http.ResponseWriter, r *http.Request) { } } - if !query.All { - // The default is get only running containers. Do this with a filterfunc + // Docker thinks that if status is given as an input, then we should override + // the all setting and always deal with all containers. + if len(query.Filters["status"]) > 0 { + all = true + } + if !all { runningOnly, err := shared.GenerateContainerFilterFuncs("status", define.ContainerStateRunning.String(), runtime) if err != nil { utils.InternalServerError(w, err) diff --git a/pkg/api/server/register_swagger.go b/pkg/api/server/register_swagger.go new file mode 100644 index 000000000..5564ec096 --- /dev/null +++ b/pkg/api/server/register_swagger.go @@ -0,0 +1,26 @@ +package server + +import ( + "net/http" + "os" + + "github.com/gorilla/mux" +) + +// DefaultPodmanSwaggerSpec provides the default path to the podman swagger spec file +const DefaultPodmanSwaggerSpec = "/usr/share/containers/podman/swagger.yaml" + +// RegisterSwaggerHandlers maps the swagger endpoint for the server +func (s *APIServer) RegisterSwaggerHandlers(r *mux.Router) error { + // This handler does _*NOT*_ provide an UI rather just a swagger spec that an UI could render + r.PathPrefix("/swagger/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + path := DefaultPodmanSwaggerSpec + if p, found := os.LookupEnv("PODMAN_SWAGGER_SPEC"); found { + path = p + } + w.Header().Set("Content-Type", "text/yaml") + + http.ServeFile(w, r, path) + }) + return nil +} diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index 87b11b716..2c709bc59 100644 --- a/pkg/api/server/server.go +++ b/pkg/api/server/server.go @@ -114,6 +114,7 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li server.registerPingHandlers, server.RegisterPluginsHandlers, server.registerPodsHandlers, + server.RegisterSwaggerHandlers, server.RegisterSwarmHandlers, server.registerSystemHandlers, server.registerVersionHandlers, diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index 116af9709..f270060a6 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -130,7 +130,7 @@ func (c *Connection) DoRequest(httpBody io.Reader, httpMethod, endpoint string, // if more desirable we could use url to form the encoded endpoint with params r := req.URL.Query() for k, v := range queryParams { - r.Add(k, url.QueryEscape(v)) + r.Add(k, v) } req.URL.RawQuery = r.Encode() } @@ -155,18 +155,14 @@ func GetConnectionFromContext(ctx context.Context) (*Connection, error) { return conn, nil } -// FiltersToHTML converts our typical filter format of a +// FiltersToString converts our typical filter format of a // map[string][]string to a query/html safe string. -func FiltersToHTML(filters map[string][]string) (string, error) { +func FiltersToString(filters map[string][]string) (string, error) { lowerCaseKeys := make(map[string][]string) for k, v := range filters { lowerCaseKeys[strings.ToLower(k)] = v } - unsafeString, err := jsoniter.MarshalToString(lowerCaseKeys) - if err != nil { - return "", err - } - return url.QueryEscape(unsafeString), nil + return jsoniter.MarshalToString(lowerCaseKeys) } // IsInformation returns true if the response code is 1xx diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go index 334a656d4..04f7f8802 100644 --- a/pkg/bindings/containers/containers.go +++ b/pkg/bindings/containers/containers.go @@ -5,8 +5,8 @@ import ( "net/http" "strconv" - "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" + lpapiv2 "github.com/containers/libpod/pkg/api/handlers/libpod" "github.com/containers/libpod/pkg/bindings" ) @@ -15,13 +15,16 @@ import ( // the most recent number of containers. The pod and size booleans indicate that pod information and rootfs // size information should also be included. Finally, the sync bool synchronizes the OCI runtime and // container state. -func List(ctx context.Context, filters map[string][]string, last *int, pod, size, sync *bool) ([]*shared.PsContainerOutput, error) { // nolint:typecheck +func List(ctx context.Context, filters map[string][]string, all *bool, last *int, pod, size, sync *bool) ([]lpapiv2.ListContainer, error) { // nolint:typecheck conn, err := bindings.GetConnectionFromContext(ctx) if err != nil { return nil, err } - var images []*shared.PsContainerOutput + var containers []lpapiv2.ListContainer params := make(map[string]string) + if all != nil { + params["all"] = strconv.FormatBool(*all) + } if last != nil { params["last"] = strconv.Itoa(*last) } @@ -35,7 +38,7 @@ func List(ctx context.Context, filters map[string][]string, last *int, pod, size params["sync"] = strconv.FormatBool(*sync) } if filters != nil { - filterString, err := bindings.FiltersToHTML(filters) + filterString, err := bindings.FiltersToString(filters) if err != nil { return nil, err } @@ -43,9 +46,9 @@ func List(ctx context.Context, filters map[string][]string, last *int, pod, size } response, err := conn.DoRequest(nil, http.MethodGet, "/containers/json", params) if err != nil { - return images, err + return containers, err } - return images, response.Process(nil) + return containers, response.Process(&containers) } // Prune removes stopped and exited containers from local storage. The optional filters can be @@ -62,7 +65,7 @@ func Prune(ctx context.Context, filters map[string][]string) ([]string, error) { } params := make(map[string]string) if filters != nil { - filterString, err := bindings.FiltersToHTML(filters) + filterString, err := bindings.FiltersToString(filters) if err != nil { return nil, err } diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go index deaf93f0e..b19482943 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -38,7 +38,7 @@ func List(ctx context.Context, all *bool, filters map[string][]string) ([]*handl params["all"] = strconv.FormatBool(*all) } if filters != nil { - strFilters, err := bindings.FiltersToHTML(filters) + strFilters, err := bindings.FiltersToString(filters) if err != nil { return nil, err } @@ -155,7 +155,7 @@ func Prune(ctx context.Context, filters map[string][]string) ([]string, error) { } params := make(map[string]string) if filters != nil { - stringFilter, err := bindings.FiltersToHTML(filters) + stringFilter, err := bindings.FiltersToString(filters) if err != nil { return nil, err } diff --git a/pkg/bindings/images/search.go b/pkg/bindings/images/search.go index d98ddf18d..58b25425b 100644 --- a/pkg/bindings/images/search.go +++ b/pkg/bindings/images/search.go @@ -26,7 +26,7 @@ func Search(ctx context.Context, term string, limit *int, filters map[string][]s params["limit"] = strconv.Itoa(*limit) } if filters != nil { - stringFilter, err := bindings.FiltersToHTML(filters) + stringFilter, err := bindings.FiltersToString(filters) if err != nil { return nil, err } diff --git a/pkg/bindings/pods/pods.go b/pkg/bindings/pods/pods.go index a6b74c21d..d079f01c2 100644 --- a/pkg/bindings/pods/pods.go +++ b/pkg/bindings/pods/pods.go @@ -97,7 +97,7 @@ func List(ctx context.Context, filters map[string][]string) (*[]libpod.PodInspec } params := make(map[string]string) if filters != nil { - stringFilter, err := bindings.FiltersToHTML(filters) + stringFilter, err := bindings.FiltersToString(filters) if err != nil { return nil, err } diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go index 96786223d..6c5b7978c 100644 --- a/pkg/cgroups/cgroups.go +++ b/pkg/cgroups/cgroups.go @@ -536,15 +536,14 @@ func (c *CgroupControl) Stat() (*Metrics, error) { return &m, nil } -func readCgroup2MapFile(ctr *CgroupControl, name string) (map[string][]string, error) { +func readCgroup2MapPath(path string) (map[string][]string, error) { ret := map[string][]string{} - p := filepath.Join(cgroupRoot, ctr.path, name) - f, err := os.Open(p) + f, err := os.Open(path) if err != nil { if os.IsNotExist(err) { return ret, nil } - return nil, errors.Wrapf(err, "open file %s", p) + return nil, errors.Wrapf(err, "open file %s", path) } defer f.Close() scanner := bufio.NewScanner(f) @@ -557,7 +556,13 @@ func readCgroup2MapFile(ctr *CgroupControl, name string) (map[string][]string, e ret[parts[0]] = parts[1:] } if err := scanner.Err(); err != nil { - return nil, errors.Wrapf(err, "parsing file %s", p) + return nil, errors.Wrapf(err, "parsing file %s", path) } return ret, nil } + +func readCgroup2MapFile(ctr *CgroupControl, name string) (map[string][]string, error) { + p := filepath.Join(cgroupRoot, ctr.path, name) + + return readCgroup2MapPath(p) +} diff --git a/pkg/cgroups/cpu.go b/pkg/cgroups/cpu.go index a43a76b22..5f0a18031 100644 --- a/pkg/cgroups/cpu.go +++ b/pkg/cgroups/cpu.go @@ -121,3 +121,42 @@ func (c *cpuHandler) Stat(ctr *CgroupControl, m *Metrics) error { m.CPU = CPUMetrics{Usage: usage} return nil } + +// GetSystemCPUUsage returns the system usage for all the cgroups +func GetSystemCPUUsage() (uint64, error) { + cgroupv2, err := IsCgroup2UnifiedMode() + if err != nil { + return 0, err + } + if !cgroupv2 { + p := filepath.Join(cgroupRoot, CPUAcct, "cpuacct.usage") + return readFileAsUint64(p) + } + + files, err := ioutil.ReadDir(cgroupRoot) + if err != nil { + return 0, errors.Wrapf(err, "read directory %q", cgroupRoot) + } + var total uint64 + for _, file := range files { + if !file.IsDir() { + continue + } + p := filepath.Join(cgroupRoot, file.Name(), "cpu.stat") + + values, err := readCgroup2MapPath(p) + if err != nil { + return 0, err + } + + if val, found := values["usage_usec"]; found { + v, err := strconv.ParseUint(cleanString(val[0]), 10, 0) + if err != nil { + return 0, err + } + total += v * 1000 + } + + } + return total, nil +} diff --git a/pkg/rootlessport/rootlessport_linux.go b/pkg/rootlessport/rootlessport_linux.go index 3e678d33a..2b51f4e09 100644 --- a/pkg/rootlessport/rootlessport_linux.go +++ b/pkg/rootlessport/rootlessport_linux.go @@ -122,6 +122,7 @@ func parent() error { logrus.WithError(driverErr).Warn("parent driver exited") } errCh <- driverErr + close(errCh) }() opaque := driver.OpaqueForChild() logrus.Infof("opaque=%+v", opaque) @@ -142,15 +143,12 @@ func parent() error { }() // reexec the child process in the child netns - cmd := exec.Command(fmt.Sprintf("/proc/%d/exe", os.Getpid())) + cmd := exec.Command("/proc/self/exe") cmd.Args = []string{reexecChildKey} cmd.Stdin = childQuitR cmd.Stdout = &logrusWriter{prefix: "child"} cmd.Stderr = cmd.Stdout cmd.Env = append(os.Environ(), reexecChildEnvOpaque+"="+string(opaqueJSON)) - cmd.SysProcAttr = &syscall.SysProcAttr{ - Pdeathsig: syscall.SIGTERM, - } childNS, err := ns.GetNS(cfg.NetNSPath) if err != nil { return err @@ -162,14 +160,27 @@ func parent() error { return err } + defer func() { + if err := syscall.Kill(cmd.Process.Pid, syscall.SIGTERM); err != nil { + logrus.WithError(err).Warn("kill child process") + } + }() + logrus.Info("waiting for initComplete") // wait for the child to connect to the parent - select { - case <-initComplete: - logrus.Infof("initComplete is closed; parent and child established the communication channel") - case err := <-errCh: - return err +outer: + for { + select { + case <-initComplete: + logrus.Infof("initComplete is closed; parent and child established the communication channel") + break outer + case err := <-errCh: + if err != nil { + return err + } + } } + defer func() { logrus.Info("stopping parent driver") quit <- struct{}{} diff --git a/pkg/spec/config_linux.go b/pkg/spec/config_linux.go index 32d8cb4de..5f39b6d0d 100644 --- a/pkg/spec/config_linux.go +++ b/pkg/spec/config_linux.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strconv" "strings" "github.com/containers/libpod/pkg/rootless" @@ -90,6 +91,42 @@ func devicesFromPath(g *generate.Generator, devicePath string) error { return addDevice(g, strings.Join(append([]string{resolvedDevicePath}, devs[1:]...), ":")) } +func deviceCgroupRules(g *generate.Generator, deviceCgroupRules []string) error { + for _, deviceCgroupRule := range deviceCgroupRules { + if err := validateDeviceCgroupRule(deviceCgroupRule); err != nil { + return err + } + ss := parseDeviceCgroupRule(deviceCgroupRule) + if len(ss[0]) != 5 { + return errors.Errorf("invalid device cgroup rule format: '%s'", deviceCgroupRule) + } + matches := ss[0] + var major, minor *int64 + if matches[2] == "*" { + majorDev := int64(-1) + major = &majorDev + } else { + majorDev, err := strconv.ParseInt(matches[2], 10, 64) + if err != nil { + return errors.Errorf("invalid major value in device cgroup rule format: '%s'", deviceCgroupRule) + } + major = &majorDev + } + if matches[3] == "*" { + minorDev := int64(-1) + minor = &minorDev + } else { + minorDev, err := strconv.ParseInt(matches[2], 10, 64) + if err != nil { + return errors.Errorf("invalid major value in device cgroup rule format: '%s'", deviceCgroupRule) + } + minor = &minorDev + } + g.AddLinuxResourcesDevice(true, matches[1], major, minor, matches[4]) + } + return nil +} + func addDevice(g *generate.Generator, device string) error { src, dst, permissions, err := ParseDevice(device) if err != nil { diff --git a/pkg/spec/config_unsupported.go b/pkg/spec/config_unsupported.go index a2c7f4416..be3e7046d 100644 --- a/pkg/spec/config_unsupported.go +++ b/pkg/spec/config_unsupported.go @@ -30,3 +30,7 @@ func makeThrottleArray(throttleInput []string, rateType int) ([]spec.LinuxThrott func devicesFromPath(g *generate.Generator, devicePath string) error { return errors.New("function not implemented") } + +func deviceCgroupRules(g *generate.Generator, deviceCgroupRules []string) error { + return errors.New("function not implemented") +} diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go index 173dfb842..8010be0d4 100644 --- a/pkg/spec/createconfig.go +++ b/pkg/spec/createconfig.go @@ -38,6 +38,7 @@ type CreateResourceConfig struct { CPUs float64 // cpus CPUsetCPUs string CPUsetMems string // cpuset-mems + DeviceCgroupRules []string //device-cgroup-rule DeviceReadBps []string // device-read-bps DeviceReadIOps []string // device-read-iops DeviceWriteBps []string // device-write-bps diff --git a/pkg/spec/parse.go b/pkg/spec/parse.go index 6fa0b0636..a5dfccdb9 100644 --- a/pkg/spec/parse.go +++ b/pkg/spec/parse.go @@ -2,12 +2,17 @@ package createconfig import ( "fmt" + "regexp" "strconv" "strings" "github.com/docker/go-units" + "github.com/pkg/errors" ) +// deviceCgroupRulegex defines the valid format of device-cgroup-rule +var deviceCgroupRuleRegex = regexp.MustCompile(`^([acb]) ([0-9]+|\*):([0-9]+|\*) ([rwm]{1,3})$`) + // Pod signifies a kernel namespace is being shared // by a container with the pod it is associated with const Pod = "pod" @@ -205,3 +210,16 @@ func IsValidDeviceMode(mode string) bool { } return true } + +// validateDeviceCgroupRule validates the format of deviceCgroupRule +func validateDeviceCgroupRule(deviceCgroupRule string) error { + if !deviceCgroupRuleRegex.MatchString(deviceCgroupRule) { + return errors.Errorf("invalid device cgroup rule format: '%s'", deviceCgroupRule) + } + return nil +} + +// parseDeviceCgroupRule matches and parses the deviceCgroupRule into slice +func parseDeviceCgroupRule(deviceCgroupRule string) [][]string { + return deviceCgroupRuleRegex.FindAllStringSubmatch(deviceCgroupRule, -1) +} diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go index cae055bb0..b2a152a2d 100644 --- a/pkg/spec/spec.go +++ b/pkg/spec/spec.go @@ -232,6 +232,12 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM return nil, err } } + if len(config.Resources.DeviceCgroupRules) != 0 { + if err := deviceCgroupRules(&g, config.Resources.DeviceCgroupRules); err != nil { + return nil, err + } + addedResources = true + } } // SECURITY OPTS |