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 }