-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Initial podman support #2794
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial podman support #2794
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. | ||
towe75 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
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 | ||
towe75 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
func Client() (*dclient.Client, error) { | ||
towe75 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
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 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
// Copyright 2014 Google Inc. All Rights Reserved. | ||
towe75 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
// | ||
// 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") | ||
towe75 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
// 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 | ||
towe75 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would expected some context to be added before returning the error. I'm not sure if using named returned valued does not decrease code readability. |
||
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 { | ||
towe75 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// 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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are two potentially different scenarios:
Can you return more specific error, please (hint: |
||
} | ||
|
||
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) | ||
) | ||
|
||
Comment on lines
+170
to
+176
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure if it is necessary to rely on regular expressions to parse versions. As far as I understand we expect them to be strings received from Podman API so perhaps splitting these strings an calling |
||
// 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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider wrapping the error. |
||
} | ||
|
||
podmanInfo, err := ValidateInfo() | ||
if err != nil { | ||
return fmt.Errorf("failed to validate Podman info: %v", err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto. |
||
} | ||
|
||
// Version already validated above, assume no error here. | ||
podmanVersion, _ := parseVersion(podmanInfo.ServerVersion, versionRe, 3) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the version is already validated then why don't you just split a string? Regular expression might be an overkill in this scenario. |
||
|
||
podmanAPIVersion, _ := APIVersion() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why have you decided to ignore the error? You can assume this call will never fail. |
||
|
||
cgroupSubsystems, err := libcontainer.GetCgroupSubsystems(includedMetrics) | ||
if err != nil { | ||
return fmt.Errorf("failed to get cgroup subsystems: %v", err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrap an error, please. |
||
} | ||
|
||
klog.V(1).Infof("Registering Podman factory") | ||
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 | ||
} |
Uh oh!
There was an error while loading. Please reload this page.