From 99129cdd83c5d7fa2667365a191289646b99ae1c Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Sun, 31 Jan 2021 16:59:53 +0100 Subject: [PATCH 1/7] Initial podman support --- cmd/internal/container/install/install.go | 1 + container/container.go | 1 + container/podman/client.go | 61 +++++ container/podman/factory.go | 210 ++++++++++++++++ container/podman/handler.go | 288 ++++++++++++++++++++++ container/podman/install/install.go | 29 +++ container/podman/plugin.go | 77 ++++++ container/podman/podman.go | 200 +++++++++++++++ fs/types.go | 7 + info/v1/podman.go | 38 +++ 10 files changed, 912 insertions(+) create mode 100644 container/podman/client.go create mode 100644 container/podman/factory.go create mode 100644 container/podman/handler.go create mode 100644 container/podman/install/install.go create mode 100644 container/podman/plugin.go create mode 100644 container/podman/podman.go create mode 100644 info/v1/podman.go diff --git a/cmd/internal/container/install/install.go b/cmd/internal/container/install/install.go index ec0dd0bc5b..ef6cb7506b 100644 --- a/cmd/internal/container/install/install.go +++ b/cmd/internal/container/install/install.go @@ -20,5 +20,6 @@ import ( _ "github.com/google/cadvisor/container/containerd/install" _ "github.com/google/cadvisor/container/crio/install" _ "github.com/google/cadvisor/container/docker/install" + _ "github.com/google/cadvisor/container/podman/install" _ "github.com/google/cadvisor/container/systemd/install" ) diff --git a/container/container.go b/container/container.go index 8414efc6a0..4c435a0e81 100644 --- a/container/container.go +++ b/container/container.go @@ -35,6 +35,7 @@ const ( ContainerTypeCrio ContainerTypeContainerd ContainerTypeMesos + ContainerTypePodman ) // Interface for container operation handlers. diff --git a/container/podman/client.go b/container/podman/client.go new file mode 100644 index 0000000000..09cda8fc69 --- /dev/null +++ b/container/podman/client.go @@ -0,0 +1,61 @@ +// Copyright 2015 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. + +// Handler for /validate content. +// Validates cadvisor dependencies - kernel, os, docker setup. + +package podman + +import ( + "net/http" + "sync" + + dclient "github.com/docker/docker/client" + "github.com/docker/go-connections/tlsconfig" +) + +var ( + podmanClient *dclient.Client + podmanClientErr error + podmanClientOnce sync.Once +) + +// Client creates a Docker API client based on the given Docker flags +func Client() (*dclient.Client, error) { + podmanClientOnce.Do(func() { + var client *http.Client + if *ArgPodmanTLS { + client = &http.Client{} + options := tlsconfig.Options{ + CAFile: *ArgPodmanCA, + CertFile: *ArgPodmanCert, + KeyFile: *ArgPodmanKey, + InsecureSkipVerify: false, + } + tlsc, err := tlsconfig.Client(options) + if err != nil { + podmanClientErr = err + return + } + client.Transport = &http.Transport{ + TLSClientConfig: tlsc, + } + } + podmanClient, podmanClientErr = dclient.NewClientWithOpts( + dclient.WithHost(*ArgPodmanEndpoint), + dclient.WithHTTPClient(client), + dclient.WithAPIVersionNegotiation()) + }) + return podmanClient, podmanClientErr +} diff --git a/container/podman/factory.go b/container/podman/factory.go new file mode 100644 index 0000000000..e4c0e5e37f --- /dev/null +++ b/container/podman/factory.go @@ -0,0 +1,210 @@ +// Copyright 2014 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 ( + "flag" + "fmt" + "path" + "regexp" + "strings" + "sync" + "time" + + "github.com/google/cadvisor/container" + "github.com/google/cadvisor/container/libcontainer" + "github.com/google/cadvisor/fs" + info "github.com/google/cadvisor/info/v1" + "github.com/google/cadvisor/watcher" + + docker "github.com/docker/docker/client" + "golang.org/x/net/context" + "k8s.io/klog/v2" +) + +var ArgPodmanEndpoint = flag.String("podman", "unix:///run/podman/podman.sock", "podman endpoint") +var ArgPodmanTLS = flag.Bool("podman-tls", false, "use TLS to connect to podman") +var ArgPodmanCert = flag.String("podman-tls-cert", "cert.pem", "path to client certificate") +var ArgPodmanKey = flag.String("podman-tls-key", "key.pem", "path to private key") +var ArgPodmanCA = flag.String("podman-tls-ca", "ca.pem", "path to trusted CA") + +// The namespace under which podman aliases are unique. +const PodmanNamespace = "podman" + +// The retry times for getting podman root dir +const rootDirRetries = 5 + +//The retry period for getting podman root dir, Millisecond +const rootDirRetryPeriod time.Duration = 1000 * time.Millisecond + +// Regexp that identifies podman cgroups +// --cgroup-parent have another prefix than 'libpod' +var podmanCgroupRegexp = regexp.MustCompile(`([a-z0-9]{64})`) + +var podmanEnvWhitelist = flag.String("podman_env_metadata_whitelist", "", "a comma-separated list of environment variable keys matched with specified prefix that needs to be collected for podman containers") + +var ( + // Basepath to all container specific information that libcontainer stores. + podmanRootDir string + + podmanRootDirOnce sync.Once +) + +func RootDir() string { + podmanRootDirOnce.Do(func() { + for i := 0; i < rootDirRetries; i++ { + status, err := Status() + if err == nil && status.RootDir != "" { + podmanRootDir = status.RootDir + break + } else { + time.Sleep(rootDirRetryPeriod) + } + } + }) + return podmanRootDir +} + +type podmanFactory struct { + machineInfoFactory info.MachineInfoFactory + + client *docker.Client + + // Information about the mounted cgroup subsystems. + cgroupSubsystems libcontainer.CgroupSubsystems + + // Information about mounted filesystems. + fsInfo fs.FsInfo + + podmanVersion []int + + podmanAPIVersion []int + + includedMetrics container.MetricSet +} + +func (f *podmanFactory) String() string { + return PodmanNamespace +} + +func (f *podmanFactory) NewContainerHandler(name string, inHostNamespace bool) (handler container.ContainerHandler, err error) { + client, err := Client() + if err != nil { + return + } + + metadataEnvs := strings.Split(*podmanEnvWhitelist, ",") + + handler, err = newPodmanContainerHandler( + client, + name, + f.machineInfoFactory, + f.fsInfo, + &f.cgroupSubsystems, + inHostNamespace, + metadataEnvs, + f.podmanVersion, + f.includedMetrics, + ) + return +} + +// Returns the Podman ID from the full container name. +func ContainerNameToPodmanId(name string) string { + id := path.Base(name) + + if matches := podmanCgroupRegexp.FindStringSubmatch(id); matches != nil { + return matches[1] + } + + return id +} + +// isContainerName returns true if the cgroup with associated name +// corresponds to a podman container. +func isContainerName(name string) bool { + // always ignore .mount cgroup even if associated with podman and delegate to systemd + if strings.HasSuffix(name, ".mount") { + return false + } + return podmanCgroupRegexp.MatchString(path.Base(name)) +} + +// Podman handles all containers prefixed with libpod- +func (f *podmanFactory) CanHandleAndAccept(name string) (bool, bool, error) { + // if the container is not associated with podman, we can't handle it or accept it. + if !isContainerName(name) { + return false, false, nil + } + + // Check if the container is known to podman and it is active. + id := ContainerNameToPodmanId(name) + + // We assume that if Inspect fails then the container is not known to podman. + ctnr, err := f.client.ContainerInspect(context.Background(), id) + if err != nil || !ctnr.State.Running { + return false, true, fmt.Errorf("error inspecting container: %v", err) + } + + return true, true, nil +} + +func (f *podmanFactory) DebugInfo() map[string][]string { + return map[string][]string{} +} + +var ( + versionRegexpString = `(\d+)\.(\d+)\.(\d+)` + versionRe = regexp.MustCompile(versionRegexpString) + apiVersionRegexpString = `(\d+)\.(\d+)` + apiVersionRe = regexp.MustCompile(apiVersionRegexpString) +) + +// Register root container before running this function! +func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, includedMetrics container.MetricSet) error { + client, err := Client() + if err != nil { + return fmt.Errorf("unable to communicate with podman: %v", err) + } + + podmanInfo, err := ValidateInfo() + if err != nil { + return fmt.Errorf("failed to validate Podman info: %v", err) + } + + // Version already validated above, assume no error here. + podmanVersion, _ := parseVersion(podmanInfo.ServerVersion, versionRe, 3) + + podmanAPIVersion, _ := APIVersion() + + cgroupSubsystems, err := libcontainer.GetCgroupSubsystems(includedMetrics) + if err != nil { + return fmt.Errorf("failed to get cgroup subsystems: %v", err) + } + + klog.V(1).Infof("Registering Podman factory, version %s", podmanVersion) + f := &podmanFactory{ + cgroupSubsystems: cgroupSubsystems, + client: client, + podmanVersion: podmanVersion, + podmanAPIVersion: podmanAPIVersion, + fsInfo: fsInfo, + machineInfoFactory: factory, + includedMetrics: includedMetrics, + } + + container.RegisterContainerHandlerFactory(f, []watcher.ContainerWatchSource{watcher.Raw}) + return nil +} diff --git a/container/podman/handler.go b/container/podman/handler.go new file mode 100644 index 0000000000..1f5aee7b9b --- /dev/null +++ b/container/podman/handler.go @@ -0,0 +1,288 @@ +// Copyright 2014 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. + +// Handler for Podman containers. +package podman + +import ( + "fmt" + "strconv" + "strings" + "time" + + "github.com/google/cadvisor/container" + "github.com/google/cadvisor/container/common" + containerlibcontainer "github.com/google/cadvisor/container/libcontainer" + "github.com/google/cadvisor/fs" + info "github.com/google/cadvisor/info/v1" + + dockercontainer "github.com/docker/docker/api/types/container" + docker "github.com/docker/docker/client" + "golang.org/x/net/context" +) + +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 + + fsInfo fs.FsInfo + + // Time at which this container was created. + creationTime time.Time + + // Metadata associated with the container. + envs map[string]string + labels map[string]string + + // Image name used for this container. + image string + + // The network mode of the container + networkMode dockercontainer.NetworkMode + + // Filesystem handler. + fsHandler common.FsHandler + + // The IP address of the container + ipAddress string + + includedMetrics container.MetricSet + + // Reference to the container + reference info.ContainerReference + + libcontainerHandler *containerlibcontainer.Handler +} + +var _ container.ContainerHandler = &podmanContainerHandler{} + +// func getRwLayerID(containerID, storageDir string, sd storageDriver, dockerVersion []int) (string, error) { +// bytes, err := ioutil.ReadFile(path.Join(storageDir, "image", string(sd), "layerdb", "mounts", containerID, rwLayerIDFile)) +// if err != nil { +// return "", fmt.Errorf("failed to identify the read-write layer ID for container %q. - %v", containerID, err) +// } +// return string(bytes), err +// } + +// newPodmanContainerHandler returns a new container.ContainerHandler +func newPodmanContainerHandler( + client *docker.Client, + name string, + machineInfoFactory info.MachineInfoFactory, + fsInfo fs.FsInfo, + cgroupSubsystems *containerlibcontainer.CgroupSubsystems, + inHostNamespace bool, + metadataEnvs []string, + dockerVersion []int, + includedMetrics container.MetricSet, +) (container.ContainerHandler, error) { + // Create the cgroup paths. + cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems.MountPoints, name) + + // Generate the equivalent cgroup manager for this container. + cgroupManager, err := containerlibcontainer.NewCgroupManager(name, cgroupPaths) + if err != nil { + return nil, err + } + + id := ContainerNameToPodmanId(name) + + // We assume that if Inspect fails then the container is not known to podman. + ctnr, err := client.ContainerInspect(context.Background(), id) + if err != nil { + return nil, fmt.Errorf("failed to inspect container %q: %v", id, err) + } + + rootFs := "/" + if !inHostNamespace { + rootFs = "/rootfs" + } + + handler := &podmanContainerHandler{ + machineInfoFactory: machineInfoFactory, + cgroupPaths: cgroupPaths, + fsInfo: fsInfo, + envs: make(map[string]string), + labels: ctnr.Config.Labels, + includedMetrics: includedMetrics, + } + // Timestamp returned by Podman is in time.RFC3339Nano format. + handler.creationTime, err = time.Parse(time.RFC3339Nano, ctnr.Created) + if err != nil { + // This should not happen, report the error just in case + return nil, fmt.Errorf("failed to parse the create timestamp %q for container %q: %v", ctnr.Created, id, err) + } + handler.libcontainerHandler = containerlibcontainer.NewHandler(cgroupManager, rootFs, ctnr.State.Pid, includedMetrics) + + // Add the name and bare ID as aliases of the container. + handler.reference = info.ContainerReference{ + Id: id, + Name: name, + Aliases: []string{strings.TrimPrefix(ctnr.Name, "/"), id}, + Namespace: PodmanNamespace, + } + handler.image = ctnr.Config.Image + handler.networkMode = ctnr.HostConfig.NetworkMode + // Only adds restartcount label if it's greater than 0 + if ctnr.RestartCount > 0 { + handler.labels["restartcount"] = strconv.Itoa(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 + ipAddress := ctnr.NetworkSettings.IPAddress + networkMode := string(ctnr.HostConfig.NetworkMode) + if ipAddress == "" && strings.HasPrefix(networkMode, "container:") { + containerID := strings.TrimPrefix(networkMode, "container:") + c, err := client.ContainerInspect(context.Background(), containerID) + if err != nil { + return nil, fmt.Errorf("failed to inspect container %q: %v", id, err) + } + ipAddress = c.NetworkSettings.IPAddress + } + + handler.ipAddress = ipAddress + + // split env vars to get metadata map. + for _, exposedEnv := range metadataEnvs { + if exposedEnv == "" { + // if no dockerEnvWhitelist provided, len(metadataEnvs) == 1, metadataEnvs[0] == "" + 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 (h *podmanContainerHandler) Start() { + if h.fsHandler != nil { + h.fsHandler.Start() + } +} + +func (h *podmanContainerHandler) Cleanup() { + if h.fsHandler != nil { + h.fsHandler.Stop() + } +} + +func (h *podmanContainerHandler) ContainerReference() (info.ContainerReference, error) { + return h.reference, nil +} + +func (h *podmanContainerHandler) needNet() bool { + if h.includedMetrics.Has(container.NetworkUsageMetrics) { + return !h.networkMode.IsContainer() + } + return false +} + +func (h *podmanContainerHandler) GetSpec() (info.ContainerSpec, error) { + // FIXME: collect FS metrics + hasFilesystem := false + // we optionally collect disk usage metrics + // if includedMetrics.Has(container.DiskUsageMetrics) { + // handler.fsHandler = common.NewFsHandler(common.DefaultPeriod, rootfsStorageDir, storageLogDir, fsInfo) + + spec, err := common.GetSpec(h.cgroupPaths, h.machineInfoFactory, h.needNet(), hasFilesystem) + + spec.Labels = h.labels + spec.Envs = h.envs + spec.Image = h.image + spec.CreationTime = h.creationTime + + return spec, err +} + +func (h *podmanContainerHandler) getFsStats(stats *info.ContainerStats) error { + mi, err := h.machineInfoFactory.GetMachineInfo() + if err != nil { + return err + } + + if h.includedMetrics.Has(container.DiskIOMetrics) { + common.AssignDeviceNamesToDiskStats((*common.MachineInfoNamer)(mi), &stats.DiskIo) + } + + return nil +} + +func (h *podmanContainerHandler) GetStats() (*info.ContainerStats, error) { + stats, err := h.libcontainerHandler.GetStats() + if err != nil { + return stats, err + } + // Clean up stats for containers that don't have their own network - this + // includes containers running in Kubernetes pods that use the network of the + // infrastructure container. This stops metrics being reported multiple times + // for each container in a pod. + if !h.needNet() { + stats.Network = info.NetworkStats{} + } + + // Get filesystem stats. + err = h.getFsStats(stats) + if err != nil { + return stats, err + } + + return stats, nil +} + +func (h *podmanContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) { + // No-op for Podman driver. + return []info.ContainerReference{}, nil +} + +func (h *podmanContainerHandler) GetCgroupPath(resource string) (string, error) { + path, ok := h.cgroupPaths[resource] + if !ok { + return "", fmt.Errorf("could not find path for resource %q for container %q", resource, h.reference.Name) + } + return path, nil +} + +func (h *podmanContainerHandler) GetContainerLabels() map[string]string { + return h.labels +} + +func (h *podmanContainerHandler) GetContainerIPAddress() string { + return h.ipAddress +} + +func (h *podmanContainerHandler) ListProcesses(listType container.ListType) ([]int, error) { + return h.libcontainerHandler.GetProcesses() +} + +func (h *podmanContainerHandler) Exists() bool { + return common.CgroupExists(h.cgroupPaths) +} + +func (h *podmanContainerHandler) Type() container.ContainerType { + return container.ContainerTypePodman +} diff --git a/container/podman/install/install.go b/container/podman/install/install.go new file mode 100644 index 0000000000..e6210f6832 --- /dev/null +++ b/container/podman/install/install.go @@ -0,0 +1,29 @@ +// Copyright 2019 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. + +// The install package registers docker.NewPlugin() as the "docker" container provider when imported +package install + +import ( + "github.com/google/cadvisor/container" + "github.com/google/cadvisor/container/podman" + "k8s.io/klog/v2" +) + +func init() { + err := container.RegisterPlugin("podman", podman.NewPlugin()) + if err != nil { + klog.Fatalf("Failed to register podman plugin: %v", err) + } +} diff --git a/container/podman/plugin.go b/container/podman/plugin.go new file mode 100644 index 0000000000..3646e78630 --- /dev/null +++ b/container/podman/plugin.go @@ -0,0 +1,77 @@ +// Copyright 2019 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 ( + "time" + + "github.com/google/cadvisor/container" + "github.com/google/cadvisor/fs" + info "github.com/google/cadvisor/info/v1" + "github.com/google/cadvisor/watcher" + "golang.org/x/net/context" + "k8s.io/klog/v2" +) + +const podmanClientTimeout = 10 * time.Second + +// NewPlugin returns an implementation of container.Plugin suitable for passing to container.RegisterPlugin() +func NewPlugin() container.Plugin { + return &plugin{} +} + +type plugin struct{} + +func (p *plugin) InitializeFSContext(context *fs.Context) error { + SetTimeout(podmanClientTimeout) + // Try to connect to podman indefinitely on startup. + podmanStatus := retryPodmanStatus() + context.Podman = fs.PodmanContext{ + Root: RootDir(), + Driver: podmanStatus.Driver, + DriverStatus: podmanStatus.DriverStatus, + } + return nil +} + +func (p *plugin) Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, includedMetrics container.MetricSet) (watcher.ContainerWatcher, error) { + err := Register(factory, fsInfo, includedMetrics) + return nil, err +} + +func retryPodmanStatus() info.PodmanStatus { + startupTimeout := podmanClientTimeout + maxTimeout := 4 * startupTimeout + for { + ctx, _ := context.WithTimeout(context.Background(), startupTimeout) + podmanStatus, err := StatusWithContext(ctx) + if err == nil { + return podmanStatus + } + + switch err { + case context.DeadlineExceeded: + klog.Warningf("Timeout trying to communicate with podman during initialization, will retry") + default: + klog.V(5).Infof("Podman not connected: %v", err) + return info.PodmanStatus{} + } + + startupTimeout = 2 * startupTimeout + if startupTimeout > maxTimeout { + startupTimeout = maxTimeout + } + } +} diff --git a/container/podman/podman.go b/container/podman/podman.go new file mode 100644 index 0000000000..c2706ca449 --- /dev/null +++ b/container/podman/podman.go @@ -0,0 +1,200 @@ +// Copyright 2016 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. + +// Provides global docker information. +package podman + +import ( + "fmt" + "regexp" + "strconv" + + dockertypes "github.com/docker/docker/api/types" + "golang.org/x/net/context" + + "time" + + "github.com/google/cadvisor/info/v1" + "github.com/google/cadvisor/machine" +) + +var podmanTimeout = 10 * time.Second + +func defaultContext() context.Context { + ctx, _ := context.WithTimeout(context.Background(), podmanTimeout) + return ctx +} + +func SetTimeout(timeout time.Duration) { + podmanTimeout = timeout +} + +func Status() (v1.PodmanStatus, error) { + return StatusWithContext(defaultContext()) +} + +func StatusWithContext(ctx context.Context) (v1.PodmanStatus, error) { + client, err := Client() + if err != nil { + return v1.PodmanStatus{}, fmt.Errorf("unable to communicate with docker daemon: %v", err) + } + podmanInfo, err := client.Info(ctx) + if err != nil { + return v1.PodmanStatus{}, err + } + return StatusFromPodmanInfo(podmanInfo) +} + +func StatusFromPodmanInfo(podmanInfo dockertypes.Info) (v1.PodmanStatus, error) { + out := v1.PodmanStatus{} + out.KernelVersion = machine.KernelVersion() + out.OS = podmanInfo.OperatingSystem + out.Hostname = podmanInfo.Name + out.RootDir = podmanInfo.DockerRootDir + out.Driver = podmanInfo.Driver + out.NumImages = podmanInfo.Images + out.NumContainers = podmanInfo.Containers + out.DriverStatus = make(map[string]string, len(podmanInfo.DriverStatus)) + for _, v := range podmanInfo.DriverStatus { + out.DriverStatus[v[0]] = v[1] + } + var err error + ver, err := VersionString() + if err != nil { + return out, err + } + out.Version = ver + ver, err = APIVersionString() + if err != nil { + return out, err + } + out.APIVersion = ver + return out, nil +} + +func Images() ([]v1.PodmanImage, error) { + client, err := Client() + if err != nil { + return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err) + } + images, err := client.ImageList(defaultContext(), dockertypes.ImageListOptions{All: false}) + if err != nil { + return nil, err + } + + out := []v1.PodmanImage{} + const unknownTag = ":" + for _, image := range images { + if len(image.RepoTags) == 1 && image.RepoTags[0] == unknownTag { + // images with repo or tags are uninteresting. + continue + } + di := v1.PodmanImage{ + ID: image.ID, + RepoTags: image.RepoTags, + Created: image.Created, + VirtualSize: image.VirtualSize, + Size: image.Size, + } + out = append(out, di) + } + return out, nil + +} + +// Checks whether the podmanInfo reflects a valid docker setup, and returns it if it does, or an +// error otherwise. +func ValidateInfo() (*dockertypes.Info, error) { + client, err := Client() + if err != nil { + return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err) + } + + podmanInfo, err := client.Info(defaultContext()) + if err != nil { + return nil, fmt.Errorf("failed to detect Docker info: %v", err) + } + + // Fall back to version API if ServerVersion is not set in info. + if podmanInfo.ServerVersion == "" { + version, err := client.ServerVersion(defaultContext()) + if err != nil { + return nil, fmt.Errorf("unable to get docker version: %v", err) + } + podmanInfo.ServerVersion = version.Version + } + version, err := parseVersion(podmanInfo.ServerVersion, versionRe, 3) + if err != nil { + return nil, err + } + + if version[0] < 1 { + return nil, fmt.Errorf("cAdvisor requires docker version %v or above but we have found version %v reported as %q", []int{1, 0, 0}, version, podmanInfo.ServerVersion) + } + + if podmanInfo.Driver == "" { + return nil, fmt.Errorf("failed to find docker storage driver") + } + + return &podmanInfo, nil +} + +func APIVersion() ([]int, error) { + ver, err := APIVersionString() + if err != nil { + return nil, err + } + return parseVersion(ver, apiVersionRe, 2) +} + +func VersionString() (string, error) { + dockerVersion := "Unknown" + client, err := Client() + if err == nil { + version, err := client.ServerVersion(defaultContext()) + if err == nil { + dockerVersion = version.Version + } + } + return dockerVersion, err +} + +func APIVersionString() (string, error) { + apiVersion := "Unknown" + client, err := Client() + if err == nil { + version, err := client.ServerVersion(defaultContext()) + if err == nil { + apiVersion = version.APIVersion + } + } + return apiVersion, err +} + +func parseVersion(versionString string, regex *regexp.Regexp, length int) ([]int, error) { + matches := regex.FindAllStringSubmatch(versionString, -1) + if len(matches) != 1 { + return nil, fmt.Errorf("version string \"%v\" doesn't match expected regular expression: \"%v\"", versionString, regex.String()) + } + versionStringArray := matches[0][1:] + versionArray := make([]int, length) + for index, versionStr := range versionStringArray { + version, err := strconv.Atoi(versionStr) + if err != nil { + return nil, fmt.Errorf("error while parsing \"%v\" in \"%v\"", versionStr, versionString) + } + versionArray[index] = version + } + return versionArray, nil +} diff --git a/fs/types.go b/fs/types.go index 93294ebcc2..4b5a8ecca7 100644 --- a/fs/types.go +++ b/fs/types.go @@ -22,6 +22,7 @@ type Context struct { // docker root directory. Docker DockerContext Crio CrioContext + Podman PodmanContext } type DockerContext struct { @@ -30,6 +31,12 @@ type DockerContext struct { DriverStatus map[string]string } +type PodmanContext struct { + Root string + Driver string + DriverStatus map[string]string +} + type CrioContext struct { Root string } diff --git a/info/v1/podman.go b/info/v1/podman.go new file mode 100644 index 0000000000..d1c3cbe9c8 --- /dev/null +++ b/info/v1/podman.go @@ -0,0 +1,38 @@ +// Copyright 2016 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. + +// Types used for docker containers. +package v1 + +type PodmanStatus struct { + Version string `json:"version"` + APIVersion string `json:"api_version"` + KernelVersion string `json:"kernel_version"` + OS string `json:"os"` + Hostname string `json:"hostname"` + RootDir string `json:"root_dir"` + Driver string `json:"driver"` + DriverStatus map[string]string `json:"driver_status"` + ExecDriver string `json:"exec_driver"` + NumImages int `json:"num_images"` + NumContainers int `json:"num_containers"` +} + +type PodmanImage struct { + ID string `json:"id"` + RepoTags []string `json:"repo_tags"` // repository name and tags. + Created int64 `json:"created"` // unix time since creation. + VirtualSize int64 `json:"virtual_size"` + Size int64 `json:"size"` +} From 98c6d73bd49f086fa967d164a4836b1dfb4c13d6 Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Sun, 31 Jan 2021 17:09:35 +0100 Subject: [PATCH 2/7] Make vet/linter happy --- container/podman/factory.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container/podman/factory.go b/container/podman/factory.go index e4c0e5e37f..c3fdcda5e4 100644 --- a/container/podman/factory.go +++ b/container/podman/factory.go @@ -194,7 +194,7 @@ func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, includedMetrics return fmt.Errorf("failed to get cgroup subsystems: %v", err) } - klog.V(1).Infof("Registering Podman factory, version %s", podmanVersion) + klog.V(1).Infof("Registering Podman factory") f := &podmanFactory{ cgroupSubsystems: cgroupSubsystems, client: client, From 3a98eeb29eedf50bf90bdf9d6bdb4b77e3719068 Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Mon, 1 Feb 2021 13:22:18 +0100 Subject: [PATCH 3/7] Review --- container/podman/client.go | 4 ++-- container/podman/factory.go | 9 ++++---- container/podman/handler.go | 12 ++-------- container/podman/install/install.go | 3 ++- container/podman/plugin.go | 3 ++- container/podman/podman.go | 35 ++--------------------------- info/v1/podman.go | 4 ++-- 7 files changed, 17 insertions(+), 53 deletions(-) diff --git a/container/podman/client.go b/container/podman/client.go index 09cda8fc69..1546b3ee6d 100644 --- a/container/podman/client.go +++ b/container/podman/client.go @@ -1,4 +1,4 @@ -// Copyright 2015 Google Inc. All Rights Reserved. +// 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. @@ -31,7 +31,7 @@ var ( podmanClientOnce sync.Once ) -// Client creates a Docker API client based on the given Docker flags +// Client creates a Docker API client based on the given Podman flags func Client() (*dclient.Client, error) { podmanClientOnce.Do(func() { var client *http.Client diff --git a/container/podman/factory.go b/container/podman/factory.go index c3fdcda5e4..8e74823f67 100644 --- a/container/podman/factory.go +++ b/container/podman/factory.go @@ -1,4 +1,4 @@ -// Copyright 2014 Google Inc. All Rights Reserved. +// 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. @@ -122,7 +122,7 @@ func (f *podmanFactory) NewContainerHandler(name string, inHostNamespace bool) ( } // Returns the Podman ID from the full container name. -func ContainerNameToPodmanId(name string) string { +func CgroupNameToPodmanId(name string) string { id := path.Base(name) if matches := podmanCgroupRegexp.FindStringSubmatch(id); matches != nil { @@ -133,7 +133,8 @@ func ContainerNameToPodmanId(name string) string { } // isContainerName returns true if the cgroup with associated name -// corresponds to a podman container. +// coult be a podman container. +// the actual decision is made by running a ContainerInspect API call func isContainerName(name string) bool { // always ignore .mount cgroup even if associated with podman and delegate to systemd if strings.HasSuffix(name, ".mount") { @@ -150,7 +151,7 @@ func (f *podmanFactory) CanHandleAndAccept(name string) (bool, bool, error) { } // Check if the container is known to podman and it is active. - id := ContainerNameToPodmanId(name) + id := CgroupNameToPodmanId(name) // We assume that if Inspect fails then the container is not known to podman. ctnr, err := f.client.ContainerInspect(context.Background(), id) diff --git a/container/podman/handler.go b/container/podman/handler.go index 1f5aee7b9b..4a154e2d2e 100644 --- a/container/podman/handler.go +++ b/container/podman/handler.go @@ -1,4 +1,4 @@ -// Copyright 2014 Google Inc. All Rights Reserved. +// 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. @@ -71,14 +71,6 @@ type podmanContainerHandler struct { var _ container.ContainerHandler = &podmanContainerHandler{} -// func getRwLayerID(containerID, storageDir string, sd storageDriver, dockerVersion []int) (string, error) { -// bytes, err := ioutil.ReadFile(path.Join(storageDir, "image", string(sd), "layerdb", "mounts", containerID, rwLayerIDFile)) -// if err != nil { -// return "", fmt.Errorf("failed to identify the read-write layer ID for container %q. - %v", containerID, err) -// } -// return string(bytes), err -// } - // newPodmanContainerHandler returns a new container.ContainerHandler func newPodmanContainerHandler( client *docker.Client, @@ -100,7 +92,7 @@ func newPodmanContainerHandler( return nil, err } - id := ContainerNameToPodmanId(name) + id := CgroupNameToPodmanId(name) // We assume that if Inspect fails then the container is not known to podman. ctnr, err := client.ContainerInspect(context.Background(), id) diff --git a/container/podman/install/install.go b/container/podman/install/install.go index e6210f6832..0ed17d9978 100644 --- a/container/podman/install/install.go +++ b/container/podman/install/install.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google Inc. All Rights Reserved. +// 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. @@ -18,6 +18,7 @@ package install import ( "github.com/google/cadvisor/container" "github.com/google/cadvisor/container/podman" + "k8s.io/klog/v2" ) diff --git a/container/podman/plugin.go b/container/podman/plugin.go index 3646e78630..46403e420f 100644 --- a/container/podman/plugin.go +++ b/container/podman/plugin.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google Inc. All Rights Reserved. +// 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. @@ -21,6 +21,7 @@ import ( "github.com/google/cadvisor/fs" info "github.com/google/cadvisor/info/v1" "github.com/google/cadvisor/watcher" + "golang.org/x/net/context" "k8s.io/klog/v2" ) diff --git a/container/podman/podman.go b/container/podman/podman.go index c2706ca449..fb2974d2b8 100644 --- a/container/podman/podman.go +++ b/container/podman/podman.go @@ -1,4 +1,4 @@ -// Copyright 2016 Google Inc. All Rights Reserved. +// 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. @@ -19,12 +19,11 @@ import ( "fmt" "regexp" "strconv" + "time" dockertypes "github.com/docker/docker/api/types" "golang.org/x/net/context" - "time" - "github.com/google/cadvisor/info/v1" "github.com/google/cadvisor/machine" ) @@ -83,36 +82,6 @@ func StatusFromPodmanInfo(podmanInfo dockertypes.Info) (v1.PodmanStatus, error) return out, nil } -func Images() ([]v1.PodmanImage, error) { - client, err := Client() - if err != nil { - return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err) - } - images, err := client.ImageList(defaultContext(), dockertypes.ImageListOptions{All: false}) - if err != nil { - return nil, err - } - - out := []v1.PodmanImage{} - const unknownTag = ":" - for _, image := range images { - if len(image.RepoTags) == 1 && image.RepoTags[0] == unknownTag { - // images with repo or tags are uninteresting. - continue - } - di := v1.PodmanImage{ - ID: image.ID, - RepoTags: image.RepoTags, - Created: image.Created, - VirtualSize: image.VirtualSize, - Size: image.Size, - } - out = append(out, di) - } - return out, nil - -} - // Checks whether the podmanInfo reflects a valid docker setup, and returns it if it does, or an // error otherwise. func ValidateInfo() (*dockertypes.Info, error) { diff --git a/info/v1/podman.go b/info/v1/podman.go index d1c3cbe9c8..a3a0129886 100644 --- a/info/v1/podman.go +++ b/info/v1/podman.go @@ -1,4 +1,4 @@ -// Copyright 2016 Google Inc. All Rights Reserved. +// 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. @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Types used for docker containers. +// Types used for podman containers. package v1 type PodmanStatus struct { From 7fe4cd1c60f7bd06b7628fb9878071a0f7a2c7bd Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Mon, 1 Feb 2021 13:26:03 +0100 Subject: [PATCH 4/7] Comment --- container/podman/client.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/container/podman/client.go b/container/podman/client.go index 1546b3ee6d..8f6482d8d6 100644 --- a/container/podman/client.go +++ b/container/podman/client.go @@ -32,6 +32,9 @@ var ( ) // Client creates a Docker API client based on the given Podman flags +// +// At this time, we're using the podmans docker compatibility API layer +// for podman containers. func Client() (*dclient.Client, error) { podmanClientOnce.Do(func() { var client *http.Client From cf729fc4ffa152ff202ed471d276d30805d167e9 Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Mon, 1 Feb 2021 20:25:21 +0100 Subject: [PATCH 5/7] Review --- container/podman/factory.go | 2 +- container/podman/podman.go | 24 ++++++++---------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/container/podman/factory.go b/container/podman/factory.go index 8e74823f67..b48b3b8b0d 100644 --- a/container/podman/factory.go +++ b/container/podman/factory.go @@ -46,7 +46,7 @@ const PodmanNamespace = "podman" // The retry times for getting podman root dir const rootDirRetries = 5 -//The retry period for getting podman root dir, Millisecond +// The retry period for getting podman root dir, Millisecond const rootDirRetryPeriod time.Duration = 1000 * time.Millisecond // Regexp that identifies podman cgroups diff --git a/container/podman/podman.go b/container/podman/podman.go index fb2974d2b8..c1c9f8f0ab 100644 --- a/container/podman/podman.go +++ b/container/podman/podman.go @@ -46,7 +46,7 @@ func Status() (v1.PodmanStatus, error) { func StatusWithContext(ctx context.Context) (v1.PodmanStatus, error) { client, err := Client() if err != nil { - return v1.PodmanStatus{}, fmt.Errorf("unable to communicate with docker daemon: %v", err) + return v1.PodmanStatus{}, fmt.Errorf("unable to communicate with podman: %v", err) } podmanInfo, err := client.Info(ctx) if err != nil { @@ -87,33 +87,25 @@ func StatusFromPodmanInfo(podmanInfo dockertypes.Info) (v1.PodmanStatus, error) func ValidateInfo() (*dockertypes.Info, error) { client, err := Client() if err != nil { - return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err) + return nil, fmt.Errorf("unable to communicate with podman: %v", err) } podmanInfo, err := client.Info(defaultContext()) if err != nil { - return nil, fmt.Errorf("failed to detect Docker info: %v", err) + return nil, fmt.Errorf("failed to detect podman info: %v", err) } // Fall back to version API if ServerVersion is not set in info. if podmanInfo.ServerVersion == "" { version, err := client.ServerVersion(defaultContext()) if err != nil { - return nil, fmt.Errorf("unable to get docker version: %v", err) + return nil, fmt.Errorf("unable to get podman version: %v", err) } podmanInfo.ServerVersion = version.Version } - version, err := parseVersion(podmanInfo.ServerVersion, versionRe, 3) - if err != nil { - return nil, err - } - - if version[0] < 1 { - return nil, fmt.Errorf("cAdvisor requires docker version %v or above but we have found version %v reported as %q", []int{1, 0, 0}, version, podmanInfo.ServerVersion) - } if podmanInfo.Driver == "" { - return nil, fmt.Errorf("failed to find docker storage driver") + return nil, fmt.Errorf("failed to find podman storage driver") } return &podmanInfo, nil @@ -128,15 +120,15 @@ func APIVersion() ([]int, error) { } func VersionString() (string, error) { - dockerVersion := "Unknown" + podmanVersion := "Unknown" client, err := Client() if err == nil { version, err := client.ServerVersion(defaultContext()) if err == nil { - dockerVersion = version.Version + podmanVersion = version.Version } } - return dockerVersion, err + return podmanVersion, err } func APIVersionString() (string, error) { From 4f2957b21af963efd8c7ccf5f37813e9e4baffc7 Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Sat, 3 Apr 2021 11:14:59 +0200 Subject: [PATCH 6/7] Review --- container/podman/client.go | 3 --- container/podman/factory.go | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/container/podman/client.go b/container/podman/client.go index 8f6482d8d6..5b1a39de2c 100644 --- a/container/podman/client.go +++ b/container/podman/client.go @@ -12,9 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Handler for /validate content. -// Validates cadvisor dependencies - kernel, os, docker setup. - package podman import ( diff --git a/container/podman/factory.go b/container/podman/factory.go index b48b3b8b0d..dcf743b0d8 100644 --- a/container/podman/factory.go +++ b/container/podman/factory.go @@ -133,7 +133,7 @@ func CgroupNameToPodmanId(name string) string { } // isContainerName returns true if the cgroup with associated name -// coult be a podman container. +// could be a podman container. // the actual decision is made by running a ContainerInspect API call func isContainerName(name string) bool { // always ignore .mount cgroup even if associated with podman and delegate to systemd From 586ceee07c20a0e5207befafafeb8661caadd6a9 Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Sat, 3 Apr 2021 11:35:19 +0200 Subject: [PATCH 7/7] Review grouped some const() and var() renamed Client() to NewClient() --- container/podman/client.go | 4 ++-- container/podman/factory.go | 39 +++++++++++++++++++------------------ container/podman/podman.go | 10 +++++----- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/container/podman/client.go b/container/podman/client.go index 5b1a39de2c..06b132c5f3 100644 --- a/container/podman/client.go +++ b/container/podman/client.go @@ -28,11 +28,11 @@ var ( podmanClientOnce sync.Once ) -// Client creates a Docker API client based on the given Podman flags +// NewClient creates a Docker API client based on the given Podman flags // // At this time, we're using the podmans docker compatibility API layer // for podman containers. -func Client() (*dclient.Client, error) { +func NewClient() (*dclient.Client, error) { podmanClientOnce.Do(func() { var client *http.Client if *ArgPodmanTLS { diff --git a/container/podman/factory.go b/container/podman/factory.go index dcf743b0d8..5276f7da22 100644 --- a/container/podman/factory.go +++ b/container/podman/factory.go @@ -34,28 +34,29 @@ import ( "k8s.io/klog/v2" ) -var ArgPodmanEndpoint = flag.String("podman", "unix:///run/podman/podman.sock", "podman endpoint") -var ArgPodmanTLS = flag.Bool("podman-tls", false, "use TLS to connect to podman") -var ArgPodmanCert = flag.String("podman-tls-cert", "cert.pem", "path to client certificate") -var ArgPodmanKey = flag.String("podman-tls-key", "key.pem", "path to private key") -var ArgPodmanCA = flag.String("podman-tls-ca", "ca.pem", "path to trusted CA") +const ( + // The namespace under which podman aliases are unique. + PodmanNamespace = "podman" -// The namespace under which podman aliases are unique. -const PodmanNamespace = "podman" + // The retry times for getting podman root dir + rootDirRetries = 5 -// The retry times for getting podman root dir -const rootDirRetries = 5 - -// The retry period for getting podman root dir, Millisecond -const rootDirRetryPeriod time.Duration = 1000 * time.Millisecond + // The retry period for getting podman root dir, Millisecond + rootDirRetryPeriod time.Duration = 1000 * time.Millisecond +) -// Regexp that identifies podman cgroups -// --cgroup-parent have another prefix than 'libpod' -var podmanCgroupRegexp = regexp.MustCompile(`([a-z0-9]{64})`) +var ( + ArgPodmanEndpoint = flag.String("podman", "unix:///run/podman/podman.sock", "podman endpoint") + ArgPodmanTLS = flag.Bool("podman-tls", false, "use TLS to connect to podman") + ArgPodmanCert = flag.String("podman-tls-cert", "cert.pem", "path to client certificate") + ArgPodmanKey = flag.String("podman-tls-key", "key.pem", "path to private key") + ArgPodmanCA = flag.String("podman-tls-ca", "ca.pem", "path to trusted CA") -var podmanEnvWhitelist = flag.String("podman_env_metadata_whitelist", "", "a comma-separated list of environment variable keys matched with specified prefix that needs to be collected for podman containers") + // Regexp that identifies podman cgroups + // --cgroup-parent have another prefix than 'libpod' + podmanCgroupRegexp = regexp.MustCompile(`([a-z0-9]{64})`) -var ( + podmanEnvWhitelist = flag.String("podman_env_metadata_whitelist", "", "a comma-separated list of environment variable keys matched with specified prefix that needs to be collected for podman containers") // Basepath to all container specific information that libcontainer stores. podmanRootDir string @@ -100,7 +101,7 @@ func (f *podmanFactory) String() string { } func (f *podmanFactory) NewContainerHandler(name string, inHostNamespace bool) (handler container.ContainerHandler, err error) { - client, err := Client() + client, err := NewClient() if err != nil { return } @@ -175,7 +176,7 @@ var ( // Register root container before running this function! func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, includedMetrics container.MetricSet) error { - client, err := Client() + client, err := NewClient() if err != nil { return fmt.Errorf("unable to communicate with podman: %v", err) } diff --git a/container/podman/podman.go b/container/podman/podman.go index c1c9f8f0ab..d7c239801d 100644 --- a/container/podman/podman.go +++ b/container/podman/podman.go @@ -44,7 +44,7 @@ func Status() (v1.PodmanStatus, error) { } func StatusWithContext(ctx context.Context) (v1.PodmanStatus, error) { - client, err := Client() + client, err := NewClient() if err != nil { return v1.PodmanStatus{}, fmt.Errorf("unable to communicate with podman: %v", err) } @@ -85,7 +85,7 @@ func StatusFromPodmanInfo(podmanInfo dockertypes.Info) (v1.PodmanStatus, error) // Checks whether the podmanInfo reflects a valid docker setup, and returns it if it does, or an // error otherwise. func ValidateInfo() (*dockertypes.Info, error) { - client, err := Client() + client, err := NewClient() if err != nil { return nil, fmt.Errorf("unable to communicate with podman: %v", err) } @@ -121,7 +121,7 @@ func APIVersion() ([]int, error) { func VersionString() (string, error) { podmanVersion := "Unknown" - client, err := Client() + client, err := NewClient() if err == nil { version, err := client.ServerVersion(defaultContext()) if err == nil { @@ -133,7 +133,7 @@ func VersionString() (string, error) { func APIVersionString() (string, error) { apiVersion := "Unknown" - client, err := Client() + client, err := NewClient() if err == nil { version, err := client.ServerVersion(defaultContext()) if err == nil { @@ -153,7 +153,7 @@ func parseVersion(versionString string, regex *regexp.Regexp, length int) ([]int for index, versionStr := range versionStringArray { version, err := strconv.Atoi(versionStr) if err != nil { - return nil, fmt.Errorf("error while parsing \"%v\" in \"%v\"", versionStr, versionString) + return nil, fmt.Errorf("error while parsing \"%v\" in \"%v\": %w", versionStr, versionString, err) } versionArray[index] = version }