github.com/google/cadvisor@v0.49.1/container/podman/handler.go (about)

     1  // Copyright 2021 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package podman
    16  
    17  import (
    18  	"fmt"
    19  	"path"
    20  	"path/filepath"
    21  	"strings"
    22  	"time"
    23  
    24  	dockercontainer "github.com/docker/docker/api/types/container"
    25  	"github.com/opencontainers/runc/libcontainer/cgroups"
    26  
    27  	"github.com/google/cadvisor/container"
    28  	"github.com/google/cadvisor/container/common"
    29  	"github.com/google/cadvisor/container/docker"
    30  	dockerutil "github.com/google/cadvisor/container/docker/utils"
    31  	containerlibcontainer "github.com/google/cadvisor/container/libcontainer"
    32  	"github.com/google/cadvisor/devicemapper"
    33  	"github.com/google/cadvisor/fs"
    34  	info "github.com/google/cadvisor/info/v1"
    35  	"github.com/google/cadvisor/zfs"
    36  )
    37  
    38  type podmanContainerHandler struct {
    39  	// machineInfoFactory provides info.MachineInfo
    40  	machineInfoFactory info.MachineInfoFactory
    41  
    42  	// Absolute path to the cgroup hierarchies of this container.
    43  	// (e.g.: "cpu" -> "/sys/fs/cgroup/cpu/test")
    44  	cgroupPaths map[string]string
    45  
    46  	storageDriver    docker.StorageDriver
    47  	fsInfo           fs.FsInfo
    48  	rootfsStorageDir string
    49  
    50  	creationTime time.Time
    51  
    52  	// Metadata associated with the container.
    53  	envs   map[string]string
    54  	labels map[string]string
    55  
    56  	image string
    57  
    58  	networkMode dockercontainer.NetworkMode
    59  
    60  	fsHandler common.FsHandler
    61  
    62  	ipAddress string
    63  
    64  	metrics container.MetricSet
    65  
    66  	thinPoolName string
    67  
    68  	zfsParent string
    69  
    70  	reference info.ContainerReference
    71  
    72  	libcontainerHandler *containerlibcontainer.Handler
    73  }
    74  
    75  func newPodmanContainerHandler(
    76  	name string,
    77  	machineInfoFactory info.MachineInfoFactory,
    78  	fsInfo fs.FsInfo,
    79  	storageDriver docker.StorageDriver,
    80  	storageDir string,
    81  	cgroupSubsystems map[string]string,
    82  	inHostNamespace bool,
    83  	metadataEnvAllowList []string,
    84  	metrics container.MetricSet,
    85  	thinPoolName string,
    86  	thinPoolWatcher *devicemapper.ThinPoolWatcher,
    87  	zfsWatcher *zfs.ZfsWatcher,
    88  ) (container.ContainerHandler, error) {
    89  	// Create the cgroup paths.
    90  	cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems, name)
    91  
    92  	cgroupManager, err := containerlibcontainer.NewCgroupManager(name, cgroupPaths)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	rootFs := "/"
    98  	if !inHostNamespace {
    99  		rootFs = "/rootfs"
   100  		storageDir = path.Join(rootFs, storageDir)
   101  	}
   102  
   103  	rootless := path.Base(name) == containerBaseName
   104  	if rootless {
   105  		name, _ = path.Split(name)
   106  	}
   107  
   108  	id := dockerutil.ContainerNameToId(name)
   109  
   110  	// We assume that if Inspect fails then the container is not known to Podman.
   111  	ctnr, err := InspectContainer(id)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	rwLayerID, err := rwLayerID(storageDriver, storageDir, id)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	rootfsStorageDir, zfsParent, zfsFilesystem, err := determineDeviceStorage(storageDriver, storageDir, rwLayerID)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  
   126  	otherStorageDir := filepath.Join(storageDir, string(storageDriver)+"-containers", id)
   127  
   128  	handler := &podmanContainerHandler{
   129  		machineInfoFactory: machineInfoFactory,
   130  		cgroupPaths:        cgroupPaths,
   131  		storageDriver:      storageDriver,
   132  		fsInfo:             fsInfo,
   133  		rootfsStorageDir:   rootfsStorageDir,
   134  		ipAddress:          ctnr.NetworkSettings.IPAddress,
   135  		envs:               make(map[string]string),
   136  		labels:             ctnr.Config.Labels,
   137  		image:              ctnr.Config.Image,
   138  		networkMode:        ctnr.HostConfig.NetworkMode,
   139  		fsHandler:          common.NewFsHandler(common.DefaultPeriod, rootfsStorageDir, otherStorageDir, fsInfo),
   140  		metrics:            metrics,
   141  		thinPoolName:       thinPoolName,
   142  		zfsParent:          zfsParent,
   143  		reference: info.ContainerReference{
   144  			Id:        id,
   145  			Name:      name,
   146  			Aliases:   []string{strings.TrimPrefix(ctnr.Name, "/"), id},
   147  			Namespace: Namespace,
   148  		},
   149  		libcontainerHandler: containerlibcontainer.NewHandler(cgroupManager, rootFs, ctnr.State.Pid, metrics),
   150  	}
   151  
   152  	handler.creationTime, err = time.Parse(time.RFC3339, ctnr.Created)
   153  	if err != nil {
   154  		return nil, fmt.Errorf("failed to parse the create timestamp %q for container %q: %v", ctnr.Created, id, err)
   155  	}
   156  
   157  	if ctnr.RestartCount > 0 {
   158  		handler.labels["restartcount"] = fmt.Sprint(ctnr.RestartCount)
   159  	}
   160  
   161  	// Obtain the IP address for the container.
   162  	// If the NetworkMode starts with 'container:' then we need to use the IP address of the container specified.
   163  	// 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
   164  	networkMode := string(handler.networkMode)
   165  	if handler.ipAddress == "" && strings.HasPrefix(networkMode, "container:") {
   166  		id := strings.TrimPrefix(networkMode, "container:")
   167  		ctnr, err := InspectContainer(id)
   168  		if err != nil {
   169  			return nil, err
   170  		}
   171  		handler.ipAddress = ctnr.NetworkSettings.IPAddress
   172  	}
   173  
   174  	if metrics.Has(container.DiskUsageMetrics) {
   175  		handler.fsHandler = &docker.FsHandler{
   176  			FsHandler:       common.NewFsHandler(common.DefaultPeriod, rootfsStorageDir, otherStorageDir, fsInfo),
   177  			ThinPoolWatcher: thinPoolWatcher,
   178  			ZfsWatcher:      zfsWatcher,
   179  			DeviceID:        ctnr.GraphDriver.Data["DeviceId"],
   180  			ZfsFilesystem:   zfsFilesystem,
   181  		}
   182  	}
   183  
   184  	// Split env vars to get metadata map.
   185  	for _, exposedEnv := range metadataEnvAllowList {
   186  		if exposedEnv == "" {
   187  			continue
   188  		}
   189  
   190  		for _, envVar := range ctnr.Config.Env {
   191  			if envVar != "" {
   192  				splits := strings.SplitN(envVar, "=", 2)
   193  				if len(splits) == 2 && strings.HasPrefix(splits[0], exposedEnv) {
   194  					handler.envs[strings.ToLower(splits[0])] = splits[1]
   195  				}
   196  			}
   197  		}
   198  	}
   199  
   200  	return handler, nil
   201  }
   202  
   203  func determineDeviceStorage(storageDriver docker.StorageDriver, storageDir string, rwLayerID string) (
   204  	rootfsStorageDir string, zfsFilesystem string, zfsParent string, err error) {
   205  	switch storageDriver {
   206  	// Podman aliased the driver names together.
   207  	case docker.OverlayStorageDriver, docker.Overlay2StorageDriver:
   208  		rootfsStorageDir = path.Join(storageDir, "overlay", rwLayerID, "diff")
   209  		return
   210  	default:
   211  		return docker.DetermineDeviceStorage(storageDriver, storageDir, rwLayerID)
   212  	}
   213  }
   214  
   215  func (p podmanContainerHandler) ContainerReference() (info.ContainerReference, error) {
   216  	return p.reference, nil
   217  }
   218  
   219  func (p podmanContainerHandler) needNet() bool {
   220  	if p.metrics.Has(container.NetworkUsageMetrics) {
   221  		p.networkMode.IsContainer()
   222  		return !p.networkMode.IsContainer()
   223  	}
   224  	return false
   225  }
   226  
   227  func (p podmanContainerHandler) GetSpec() (info.ContainerSpec, error) {
   228  	hasFilesystem := p.metrics.Has(container.DiskUsageMetrics)
   229  
   230  	spec, err := common.GetSpec(p.cgroupPaths, p.machineInfoFactory, p.needNet(), hasFilesystem)
   231  	if err != nil {
   232  		return info.ContainerSpec{}, err
   233  	}
   234  
   235  	spec.Labels = p.labels
   236  	spec.Envs = p.envs
   237  	spec.Image = p.image
   238  	spec.CreationTime = p.creationTime
   239  
   240  	return spec, nil
   241  }
   242  
   243  func (p podmanContainerHandler) GetStats() (*info.ContainerStats, error) {
   244  	stats, err := p.libcontainerHandler.GetStats()
   245  	if err != nil {
   246  		return stats, err
   247  	}
   248  
   249  	if !p.needNet() {
   250  		stats.Network = info.NetworkStats{}
   251  	}
   252  
   253  	err = docker.FsStats(stats, p.machineInfoFactory, p.metrics, p.storageDriver,
   254  		p.fsHandler, p.fsInfo, p.thinPoolName, p.rootfsStorageDir, p.zfsParent)
   255  	if err != nil {
   256  		return stats, err
   257  	}
   258  
   259  	return stats, nil
   260  }
   261  
   262  func (p podmanContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) {
   263  	return []info.ContainerReference{}, nil
   264  }
   265  
   266  func (p podmanContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
   267  	return p.libcontainerHandler.GetProcesses()
   268  }
   269  
   270  func (p podmanContainerHandler) GetCgroupPath(resource string) (string, error) {
   271  	var res string
   272  	if !cgroups.IsCgroup2UnifiedMode() {
   273  		res = resource
   274  	}
   275  	path, ok := p.cgroupPaths[res]
   276  	if !ok {
   277  		return "", fmt.Errorf("couldn't find path for resource %q for container %q", resource, p.reference.Name)
   278  	}
   279  
   280  	return path, nil
   281  }
   282  
   283  func (p podmanContainerHandler) GetContainerLabels() map[string]string {
   284  	return p.labels
   285  }
   286  
   287  func (p podmanContainerHandler) GetContainerIPAddress() string {
   288  	return p.ipAddress
   289  }
   290  
   291  func (p podmanContainerHandler) Exists() bool {
   292  	return common.CgroupExists(p.cgroupPaths)
   293  }
   294  
   295  func (p podmanContainerHandler) Cleanup() {
   296  	if p.fsHandler != nil {
   297  		p.fsHandler.Stop()
   298  	}
   299  }
   300  
   301  func (p podmanContainerHandler) Start() {
   302  	if p.fsHandler != nil {
   303  		p.fsHandler.Start()
   304  	}
   305  }
   306  
   307  func (p podmanContainerHandler) Type() container.ContainerType {
   308  	return container.ContainerTypePodman
   309  }