Skip to content

Commit

Permalink
container: Support Podman runtime.
Browse files Browse the repository at this point in the history
Signed-off-by: Paweł Szulik <pawel.szulik@intel.com>
  • Loading branch information
Paweł Szulik committed Jan 31, 2022
1 parent bf444e7 commit 1beb52a
Show file tree
Hide file tree
Showing 21 changed files with 2,871 additions and 104 deletions.
1 change: 1 addition & 0 deletions build/config/plain.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@
unset GO_FLAGS
unset PACKAGES
unset BUILD_PACKAGES
# export BUILD_PACKAGES="selinux seccomp"
unset CADVISOR_ARGS
7 changes: 3 additions & 4 deletions cmd/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@ require (
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7
github.com/influxdb/influxdb v0.9.6-0.20151125225445-9eab56311373
github.com/mesos/mesos-go v0.0.7-0.20180413204204-29de6ff97b48
github.com/pquerna/ffjson v0.0.0-20171002144729-d49c2bc1aa13 // indirect
github.com/prometheus/client_golang v1.8.0
github.com/stretchr/testify v1.7.0
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43
google.golang.org/api v0.34.0
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602
google.golang.org/api v0.44.0
gopkg.in/olivere/elastic.v2 v2.0.12
k8s.io/klog/v2 v2.4.0
k8s.io/klog/v2 v2.9.0
k8s.io/utils v0.0.0-20211116205334-6203023598ed
)
900 changes: 859 additions & 41 deletions cmd/go.sum

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions cmd/internal/container/install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,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"
)
6 changes: 4 additions & 2 deletions cmd/internal/pages/assets/html/containers.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ <h1>{{.DisplayName}}</h1>
<div class="col-sm-12">
<h4><a href="../docker">Docker Containers</a></h4>
</div>
<div class="col-sm-12">
<h4><a href="../podman">Podman Containers</a></h4>
</div>
{{end}}
{{if .Subcontainers}}
<div class="col-sm-12">
Expand Down Expand Up @@ -250,8 +253,7 @@ <h4>Top Memory Usage:
class="subcontainer-display-input"
value=10>
</h4>
<span class="subcontroller-display-block">
<span>
<span class="subcontroller-display-block"/>
<div id="memory-per-subcontainer-usage-chart">
</div>
</div>
Expand Down
8 changes: 8 additions & 0 deletions cmd/internal/pages/pages.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ func dockerHandler(containerManager manager.Manager) auth.AuthenticatedHandlerFu
}
}

func podmanHandlerNoAuth(containerManager manager.Manager) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
servePodmanPage(containerManager, w, r.URL)
}
}

// Register http handlers
func RegisterHandlersDigest(mux httpmux.Mux, containerManager manager.Manager, authenticator *auth.DigestAuth, urlBasePrefix string) error {
// Register the handler for the containers page.
Expand Down Expand Up @@ -127,9 +133,11 @@ func RegisterHandlersBasic(mux httpmux.Mux, containerManager manager.Manager, au
if authenticator != nil {
mux.HandleFunc(ContainersPage, authenticator.Wrap(containerHandler(containerManager)))
mux.HandleFunc(DockerPage, authenticator.Wrap(dockerHandler(containerManager)))
// TODO (Creatone): Add Podman.
} else {
mux.HandleFunc(ContainersPage, containerHandlerNoAuth(containerManager))
mux.HandleFunc(DockerPage, dockerHandlerNoAuth(containerManager))
mux.HandleFunc(PodmanPage, podmanHandlerNoAuth(containerManager))
}

if ContainersPage[len(ContainersPage)-1] == '/' {
Expand Down
138 changes: 138 additions & 0 deletions cmd/internal/pages/podman.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// 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 pages

import (
"fmt"
"net/http"
"net/url"
"path"
"time"

"github.com/google/cadvisor/container/podman"
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/manager"

"k8s.io/klog/v2"
)

const PodmanPage = "/podman/"

func servePodmanPage(m manager.Manager, w http.ResponseWriter, u *url.URL) {
start := time.Now()

containerName := u.Path[len(PodmanPage)-1:]
rootDir := getRootDir(containerName)

var data *pageData

if containerName == "/" {
// Scenario for all containers.
reqParams := info.ContainerInfoRequest{
NumStats: 0,
}
conts, err := m.AllPodmanContainers(&reqParams)
if err != nil {
http.Error(w, fmt.Sprintf("failed to get container %q with error: %v", containerName, err), http.StatusNotFound)
return
}
subcontainers := make([]link, 0, len(conts))
for _, cont := range conts {
subcontainers = append(subcontainers, link{
Text: getContainerDisplayName(cont.ContainerReference),
Link: path.Join(rootDir, PodmanPage, podman.ContainerNameToPodmanID(cont.ContainerReference.Name)),
})
}

// Get Podman status
status, err := podman.Status()
if err != nil {
http.Error(w, fmt.Sprintf("failed to get docker info: %v", err), http.StatusInternalServerError)
return
}

podmanStatus, driverStatus := toStatusKV(status)
// Get Podman Images
images, err := podman.Images()
if err != nil {
http.Error(w, fmt.Sprintf("failed to get podman images: %v", err), http.StatusInternalServerError)
return
}

podmanContainerText := "Podman Containers"
data = &pageData{
DisplayName: podmanContainerText,
ParentContainers: []link{
{
Text: podmanContainerText,
Link: path.Join(rootDir, PodmanPage),
}},
Subcontainers: subcontainers,
Root: rootDir,
DockerStatus: podmanStatus,
DockerDriverStatus: driverStatus,
DockerImages: images,
}
} else {
// Scenario for specific container.
reqParams := info.ContainerInfoRequest{
NumStats: 60,
}
cont, err := m.PodmanContainer(containerName[1:], &reqParams)
if err != nil {
http.Error(w, fmt.Sprintf("failed to get container %v with error: %v", containerName, err), http.StatusNotFound)
return
}
displayName := getContainerDisplayName(cont.ContainerReference)

var parentContainers []link
parentContainers = append(parentContainers, link{
Text: "Podman Containers",
Link: path.Join(rootDir, PodmanPage),
})
parentContainers = append(parentContainers, link{
Text: displayName,
Link: path.Join(rootDir, PodmanPage, podman.ContainerNameToPodmanID(cont.Name)),
})

machineInfo, err := m.GetMachineInfo()
if err != nil {
http.Error(w, fmt.Sprintf("failed to get machine info: %v", err), http.StatusInternalServerError)
return
}
data = &pageData{
DisplayName: displayName,
ContainerName: escapeContainerName(cont.Name),
ParentContainers: parentContainers,
Spec: cont.Spec,
Stats: cont.Stats,
MachineInfo: machineInfo,
ResourcesAvailable: cont.Spec.HasCpu || cont.Spec.HasMemory || cont.Spec.HasNetwork,
CpuAvailable: cont.Spec.HasCpu,
MemoryAvailable: cont.Spec.HasMemory,
NetworkAvailable: cont.Spec.HasNetwork,
FsAvailable: cont.Spec.HasFilesystem,
CustomMetricsAvailable: cont.Spec.HasCustomMetrics,
Root: rootDir,
}
}

err := pageTemplate.Execute(w, data)
if err != nil {
klog.Errorf("Failed to apply template: %s", err)
}

klog.V(5).Infof("Request took %s", time.Since(start))
}
6 changes: 3 additions & 3 deletions cmd/internal/pages/templates.go

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions container/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const (
ContainerTypeCrio
ContainerTypeContainerd
ContainerTypeMesos
ContainerTypePodman
)

// Interface for container operation handlers.
Expand Down
25 changes: 25 additions & 0 deletions container/podman/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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 (
"context"

"github.com/containers/podman/v3/pkg/bindings"
)

func Client() (context.Context, error) {
return bindings.NewConnection(context.Background(), *endpointFlag)
}
133 changes: 133 additions & 0 deletions container/podman/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// 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 (
"flag"
"path"
"regexp"
"strings"
"sync"
"time"

"github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/docker"
"github.com/google/cadvisor/devicemapper"
"github.com/google/cadvisor/fs"
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/zfs"
)

const (
rootDirRetries = 5
rootDirRetryPeriod = time.Second
)

var (
rootDirFlag = flag.String("podman_root", "/var/lib/containers", "podman diretory")
endpointFlag = flag.String("podman", "unix:///var/run/podman/podman.sock", "podman endpoint")
)

var (
cgroupRegexp = regexp.MustCompile(`([a-z0-9]{64})`)
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)
}
}
if podmanRootDir == "" {
podmanRootDir = *rootDirFlag
}
})
return podmanRootDir
}

type podmanFactory struct {
// Information about the mounted cgroup subsystems.
machineInfoFactory info.MachineInfoFactory

storageDriver docker.StorageDriver
storageDir string

cgroupSubsystem map[string]string

fsInfo fs.FsInfo

version []int

metrics container.MetricSet

thinPoolName string
thinPoolWatcher *devicemapper.ThinPoolWatcher

zfsWatcher *zfs.ZfsWatcher
}

func isContainerName(name string) bool {
if strings.HasSuffix(name, ".mount") {
return false
}

return cgroupRegexp.MatchString(path.Base(name))
}

func (f *podmanFactory) CanHandleAndAccept(name string) (handle bool, accept bool, err error) {
if !isContainerName(name) {
return false, false, nil
}

return true, true, nil
}

func (f *podmanFactory) DebugInfo() map[string][]string {
return map[string][]string{}
}

func (f *podmanFactory) String() string {
return "podman"
}

func (f *podmanFactory) NewContainerHandler(name string, metadataEnvAllowList []string, inHostNamespace bool) (handler container.ContainerHandler, err error) {
client, err := Client()
if err != nil {
return
}

// TODO(Creatone): Check metadata env list.

return newPodmanContainerHandler(client, name, f.machineInfoFactory, f.fsInfo,
f.storageDriver, f.storageDir, f.cgroupSubsystem, inHostNamespace,
metadataEnvAllowList, f.version, f.metrics, f.thinPoolName, f.thinPoolWatcher, f.zfsWatcher)
}

func ContainerNameToPodmanID(name string) string {
id := path.Base(name)

if matches := cgroupRegexp.FindStringSubmatch(id); matches != nil {
return matches[1]
}

return id
}
Loading

0 comments on commit 1beb52a

Please sign in to comment.