container/podman/handler.go (229 lines of code) (raw):

// Copyright 2021 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package podman import ( "fmt" "path" "path/filepath" "strings" "time" dockercontainer "github.com/docker/docker/api/types/container" "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/google/cadvisor/container" "github.com/google/cadvisor/container/common" "github.com/google/cadvisor/container/docker" dockerutil "github.com/google/cadvisor/container/docker/utils" containerlibcontainer "github.com/google/cadvisor/container/libcontainer" "github.com/google/cadvisor/devicemapper" "github.com/google/cadvisor/fs" info "github.com/google/cadvisor/info/v1" "github.com/google/cadvisor/zfs" ) type podmanContainerHandler struct { // machineInfoFactory provides info.MachineInfo machineInfoFactory info.MachineInfoFactory // Absolute path to the cgroup hierarchies of this container. // (e.g.: "cpu" -> "/sys/fs/cgroup/cpu/test") cgroupPaths map[string]string storageDriver docker.StorageDriver fsInfo fs.FsInfo rootfsStorageDir string creationTime time.Time // Metadata associated with the container. envs map[string]string labels map[string]string image string networkMode dockercontainer.NetworkMode fsHandler common.FsHandler ipAddress string metrics container.MetricSet thinPoolName string zfsParent string reference info.ContainerReference libcontainerHandler *containerlibcontainer.Handler } func newPodmanContainerHandler( name string, machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, storageDriver docker.StorageDriver, storageDir string, cgroupSubsystems map[string]string, inHostNamespace bool, metadataEnvAllowList []string, metrics container.MetricSet, thinPoolName string, thinPoolWatcher *devicemapper.ThinPoolWatcher, zfsWatcher *zfs.ZfsWatcher, ) (container.ContainerHandler, error) { // Create the cgroup paths. cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems, name) cgroupManager, err := containerlibcontainer.NewCgroupManager(name, cgroupPaths) if err != nil { return nil, err } rootFs := "/" if !inHostNamespace { rootFs = "/rootfs" storageDir = path.Join(rootFs, storageDir) } rootless := path.Base(name) == containerBaseName if rootless { name, _ = path.Split(name) } id := dockerutil.ContainerNameToId(name) // We assume that if Inspect fails then the container is not known to Podman. ctnr, err := InspectContainer(id) if err != nil { return nil, err } rwLayerID, err := rwLayerID(storageDriver, storageDir, id) if err != nil { return nil, err } rootfsStorageDir, zfsParent, zfsFilesystem, err := determineDeviceStorage(storageDriver, storageDir, rwLayerID) if err != nil { return nil, err } otherStorageDir := filepath.Join(storageDir, string(storageDriver)+"-containers", id) handler := &podmanContainerHandler{ machineInfoFactory: machineInfoFactory, cgroupPaths: cgroupPaths, storageDriver: storageDriver, fsInfo: fsInfo, rootfsStorageDir: rootfsStorageDir, ipAddress: ctnr.NetworkSettings.IPAddress, envs: make(map[string]string), labels: ctnr.Config.Labels, image: ctnr.Config.Image, networkMode: ctnr.HostConfig.NetworkMode, fsHandler: common.NewFsHandler(common.DefaultPeriod, rootfsStorageDir, otherStorageDir, fsInfo), metrics: metrics, thinPoolName: thinPoolName, zfsParent: zfsParent, reference: info.ContainerReference{ Id: id, Name: name, Aliases: []string{strings.TrimPrefix(ctnr.Name, "/"), id}, Namespace: Namespace, }, libcontainerHandler: containerlibcontainer.NewHandler(cgroupManager, rootFs, ctnr.State.Pid, metrics), } handler.creationTime, err = time.Parse(time.RFC3339, ctnr.Created) if err != nil { return nil, fmt.Errorf("failed to parse the create timestamp %q for container %q: %v", ctnr.Created, id, err) } if ctnr.RestartCount > 0 { handler.labels["restartcount"] = fmt.Sprint(ctnr.RestartCount) } // Obtain the IP address for the container. // If the NetworkMode starts with 'container:' then we need to use the IP address of the container specified. // This happens in cases such as kubernetes where the containers doesn't have an IP address itself and we need to use the pod's address networkMode := string(handler.networkMode) if handler.ipAddress == "" && strings.HasPrefix(networkMode, "container:") { id := strings.TrimPrefix(networkMode, "container:") ctnr, err := InspectContainer(id) if err != nil { return nil, err } handler.ipAddress = ctnr.NetworkSettings.IPAddress } if metrics.Has(container.DiskUsageMetrics) { handler.fsHandler = &docker.FsHandler{ FsHandler: common.NewFsHandler(common.DefaultPeriod, rootfsStorageDir, otherStorageDir, fsInfo), ThinPoolWatcher: thinPoolWatcher, ZfsWatcher: zfsWatcher, DeviceID: ctnr.GraphDriver.Data["DeviceId"], ZfsFilesystem: zfsFilesystem, } } // Split env vars to get metadata map. for _, exposedEnv := range metadataEnvAllowList { if exposedEnv == "" { continue } for _, envVar := range ctnr.Config.Env { if envVar != "" { splits := strings.SplitN(envVar, "=", 2) if len(splits) == 2 && strings.HasPrefix(splits[0], exposedEnv) { handler.envs[strings.ToLower(splits[0])] = splits[1] } } } } return handler, nil } func determineDeviceStorage(storageDriver docker.StorageDriver, storageDir string, rwLayerID string) ( rootfsStorageDir string, zfsFilesystem string, zfsParent string, err error) { switch storageDriver { // Podman aliased the driver names together. case docker.OverlayStorageDriver, docker.Overlay2StorageDriver: rootfsStorageDir = path.Join(storageDir, "overlay", rwLayerID, "diff") return default: return docker.DetermineDeviceStorage(storageDriver, storageDir, rwLayerID) } } func (p podmanContainerHandler) ContainerReference() (info.ContainerReference, error) { return p.reference, nil } func (p podmanContainerHandler) needNet() bool { if p.metrics.Has(container.NetworkUsageMetrics) { p.networkMode.IsContainer() return !p.networkMode.IsContainer() } return false } func (p podmanContainerHandler) GetSpec() (info.ContainerSpec, error) { hasFilesystem := p.metrics.Has(container.DiskUsageMetrics) spec, err := common.GetSpec(p.cgroupPaths, p.machineInfoFactory, p.needNet(), hasFilesystem) if err != nil { return info.ContainerSpec{}, err } spec.Labels = p.labels spec.Envs = p.envs spec.Image = p.image spec.CreationTime = p.creationTime return spec, nil } func (p podmanContainerHandler) GetStats() (*info.ContainerStats, error) { stats, err := p.libcontainerHandler.GetStats() if err != nil { return stats, err } if !p.needNet() { stats.Network = info.NetworkStats{} } err = docker.FsStats(stats, p.machineInfoFactory, p.metrics, p.storageDriver, p.fsHandler, p.fsInfo, p.thinPoolName, p.rootfsStorageDir, p.zfsParent) if err != nil { return stats, err } return stats, nil } func (p podmanContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) { return []info.ContainerReference{}, nil } func (p podmanContainerHandler) ListProcesses(listType container.ListType) ([]int, error) { return p.libcontainerHandler.GetProcesses() } func (p podmanContainerHandler) GetCgroupPath(resource string) (string, error) { var res string if !cgroups.IsCgroup2UnifiedMode() { res = resource } path, ok := p.cgroupPaths[res] if !ok { return "", fmt.Errorf("couldn't find path for resource %q for container %q", resource, p.reference.Name) } return path, nil } func (p podmanContainerHandler) GetContainerLabels() map[string]string { return p.labels } func (p podmanContainerHandler) GetContainerIPAddress() string { return p.ipAddress } func (p podmanContainerHandler) Exists() bool { return common.CgroupExists(p.cgroupPaths) } func (p podmanContainerHandler) Cleanup() { if p.fsHandler != nil { p.fsHandler.Stop() } } func (p podmanContainerHandler) Start() { if p.fsHandler != nil { p.fsHandler.Start() } } func (p podmanContainerHandler) Type() container.ContainerType { return container.ContainerTypePodman }