Skip to content

Commit 5383840

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

File tree

6 files changed

+678
-3
lines changed

6 files changed

+678
-3
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 ...
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 ...
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 ...
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

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

0 commit comments

Comments
 (0)