Skip to content

Commit cde0f22

Browse files
committed
*: add CRI-O handler
Signed-off-by: Antonio Murdaca <[email protected]>
1 parent 1df21ea commit cde0f22

File tree

6 files changed

+669
-0
lines changed

6 files changed

+669
-0
lines changed

container/container.go

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const (
3434
ContainerTypeDocker
3535
ContainerTypeRkt
3636
ContainerTypeSystemd
37+
ContainerTypeCrio
3738
)
3839

3940
// Interface for container operation handlers.

container/crio/client.go

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// Copyright 2017 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+
// Handler for CRI-O containers.
16+
package crio
17+
18+
import (
19+
"encoding/json"
20+
"fmt"
21+
"net"
22+
"net/http"
23+
"syscall"
24+
"time"
25+
)
26+
27+
const (
28+
CrioSocket = "/var/run/crio.sock"
29+
maxUnixSocketPathSize = len(syscall.RawSockaddrUnix{}.Path)
30+
)
31+
32+
type Info struct {
33+
StorageDriver string `json:"storage_driver"`
34+
StorageRoot string `json:"storage_root"`
35+
}
36+
37+
type ContainerInfo struct {
38+
Pid int `json:"pid"`
39+
Image string `json:"image"`
40+
CreatedTime int64 `json:"created_time"`
41+
Labels map[string]string `json:"labels"`
42+
Annotations map[string]string `json:"annotations"`
43+
LogPath string `json:"log_path"`
44+
Root string `json:"root"`
45+
}
46+
47+
type crioClient interface {
48+
Info() (Info, error)
49+
ContainerInfo(string) (*ContainerInfo, error)
50+
}
51+
52+
type crioClientImpl struct {
53+
client *http.Client
54+
}
55+
56+
func configureUnixTransport(tr *http.Transport, proto, addr string) error {
57+
if len(addr) > maxUnixSocketPathSize {
58+
return fmt.Errorf("Unix socket path %q is too long", addr)
59+
}
60+
// No need for compression in local communications.
61+
tr.DisableCompression = true
62+
tr.Dial = func(_, _ string) (net.Conn, error) {
63+
return net.DialTimeout(proto, addr, 32*time.Second)
64+
}
65+
return nil
66+
}
67+
68+
// Client returns a new configured CRI-O client
69+
func Client() (crioClient, error) {
70+
tr := new(http.Transport)
71+
configureUnixTransport(tr, "unix", CrioSocket)
72+
c := &http.Client{
73+
Transport: tr,
74+
}
75+
return &crioClientImpl{
76+
client: c,
77+
}, nil
78+
}
79+
80+
func getRequest(path string) (*http.Request, error) {
81+
req, err := http.NewRequest("GET", path, nil)
82+
if err != nil {
83+
return nil, err
84+
}
85+
// For local communications over a unix socket, it doesn't matter what
86+
// the host is. We just need a valid and meaningful host name.
87+
req.Host = "crio"
88+
req.URL.Host = CrioSocket
89+
req.URL.Scheme = "http"
90+
return req, nil
91+
}
92+
93+
// Info returns generic info from the CRI-O server
94+
func (c *crioClientImpl) Info() (Info, error) {
95+
info := Info{}
96+
req, err := getRequest("/info")
97+
if err != nil {
98+
return info, err
99+
}
100+
resp, err := c.client.Do(req)
101+
if err != nil {
102+
return info, err
103+
}
104+
defer resp.Body.Close()
105+
if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
106+
return info, err
107+
}
108+
return info, nil
109+
}
110+
111+
// ContainerInfo returns information about a given container
112+
func (c *crioClientImpl) ContainerInfo(id string) (*ContainerInfo, error) {
113+
req, err := getRequest("/containers/" + id)
114+
if err != nil {
115+
return nil, err
116+
}
117+
resp, err := c.client.Do(req)
118+
if err != nil {
119+
return nil, err
120+
}
121+
defer resp.Body.Close()
122+
cInfo := ContainerInfo{}
123+
if err := json.NewDecoder(resp.Body).Decode(&cInfo); err != nil {
124+
return nil, err
125+
}
126+
return &cInfo, nil
127+
}

container/crio/factory.go

+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
// Copyright 2017 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 crio
16+
17+
import (
18+
"fmt"
19+
"path"
20+
"regexp"
21+
"strings"
22+
23+
"github.com/google/cadvisor/container"
24+
"github.com/google/cadvisor/container/libcontainer"
25+
"github.com/google/cadvisor/fs"
26+
info "github.com/google/cadvisor/info/v1"
27+
"github.com/google/cadvisor/manager/watcher"
28+
29+
"github.com/golang/glog"
30+
)
31+
32+
// The namespace under which crio aliases are unique.
33+
const CrioNamespace = "crio"
34+
35+
// Regexp that identifies CRI-O cgroups
36+
var crioCgroupRegexp = regexp.MustCompile(`([a-z0-9]{64})`)
37+
38+
type storageDriver string
39+
40+
const (
41+
// TODO add full set of supported drivers in future..
42+
overlayStorageDriver storageDriver = "overlay"
43+
overlay2StorageDriver storageDriver = "overlay2"
44+
)
45+
46+
type crioFactory struct {
47+
machineInfoFactory info.MachineInfoFactory
48+
49+
storageDriver storageDriver
50+
storageDir string
51+
52+
// Information about the mounted cgroup subsystems.
53+
cgroupSubsystems libcontainer.CgroupSubsystems
54+
55+
// Information about mounted filesystems.
56+
fsInfo fs.FsInfo
57+
58+
ignoreMetrics container.MetricSet
59+
60+
client crioClient
61+
}
62+
63+
func (self *crioFactory) String() string {
64+
return CrioNamespace
65+
}
66+
67+
func (self *crioFactory) NewContainerHandler(name string, inHostNamespace bool) (handler container.ContainerHandler, err error) {
68+
client, err := Client()
69+
if err != nil {
70+
return
71+
}
72+
// TODO are there any env vars we need to white list, if so, do it here...
73+
metadataEnvs := []string{}
74+
handler, err = newCrioContainerHandler(
75+
client,
76+
name,
77+
self.machineInfoFactory,
78+
self.fsInfo,
79+
self.storageDriver,
80+
self.storageDir,
81+
&self.cgroupSubsystems,
82+
inHostNamespace,
83+
metadataEnvs,
84+
self.ignoreMetrics,
85+
)
86+
return
87+
}
88+
89+
// Returns the CRIO ID from the full container name.
90+
func ContainerNameToCrioId(name string) string {
91+
id := path.Base(name)
92+
93+
if matches := crioCgroupRegexp.FindStringSubmatch(id); matches != nil {
94+
return matches[1]
95+
}
96+
97+
return id
98+
}
99+
100+
// isContainerName returns true if the cgroup with associated name
101+
// corresponds to a crio container.
102+
func isContainerName(name string) bool {
103+
// always ignore .mount cgroup even if associated with crio and delegate to systemd
104+
if strings.HasSuffix(name, ".mount") {
105+
return false
106+
}
107+
return crioCgroupRegexp.MatchString(path.Base(name))
108+
}
109+
110+
// crio handles all containers under /crio
111+
func (self *crioFactory) CanHandleAndAccept(name string) (bool, bool, error) {
112+
if strings.HasPrefix(path.Base(name), "crio-conmon") {
113+
// TODO(runcom): should we include crio-conmon cgroups?
114+
return false, false, nil
115+
}
116+
if !strings.HasPrefix(path.Base(name), CrioNamespace) {
117+
return false, false, nil
118+
}
119+
// if the container is not associated with CRI-O, we can't handle it or accept it.
120+
if !isContainerName(name) {
121+
return false, false, nil
122+
}
123+
return true, true, nil
124+
}
125+
126+
func (self *crioFactory) DebugInfo() map[string][]string {
127+
return map[string][]string{}
128+
}
129+
130+
var (
131+
// TODO(runcom): handle versioning in CRI-O
132+
version_regexp_string = `(\d+)\.(\d+)\.(\d+)`
133+
version_re = regexp.MustCompile(version_regexp_string)
134+
apiversion_regexp_string = `(\d+)\.(\d+)`
135+
apiversion_re = regexp.MustCompile(apiversion_regexp_string)
136+
)
137+
138+
// Register root container before running this function!
139+
func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics container.MetricSet) error {
140+
client, err := Client()
141+
if err != nil {
142+
return err
143+
}
144+
145+
info, err := client.Info()
146+
if err != nil {
147+
return err
148+
}
149+
150+
// TODO determine crio version so we can work differently w/ future versions if needed
151+
152+
cgroupSubsystems, err := libcontainer.GetCgroupSubsystems()
153+
if err != nil {
154+
return fmt.Errorf("failed to get cgroup subsystems: %v", err)
155+
}
156+
157+
glog.Infof("Registering CRI-O factory")
158+
f := &crioFactory{
159+
client: client,
160+
cgroupSubsystems: cgroupSubsystems,
161+
fsInfo: fsInfo,
162+
machineInfoFactory: factory,
163+
storageDriver: storageDriver(info.StorageDriver),
164+
storageDir: info.StorageRoot,
165+
ignoreMetrics: ignoreMetrics,
166+
}
167+
168+
container.RegisterContainerHandlerFactory(f, []watcher.ContainerWatchSource{watcher.Raw})
169+
return nil
170+
}

0 commit comments

Comments
 (0)