Skip to content

Commit e8dbd50

Browse files
Merge pull request #1741 from runcom/crio-handler
*: add CRI-O handler
2 parents 1df21ea + 4b002b3 commit e8dbd50

File tree

9 files changed

+892
-0
lines changed

9 files changed

+892
-0
lines changed

Diff for: 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.

Diff for: container/crio/client.go

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

Diff for: container/crio/client_test.go

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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 "fmt"
18+
19+
type crioClientMock struct {
20+
info Info
21+
containersInfo map[string]*ContainerInfo
22+
err error
23+
}
24+
25+
func (c *crioClientMock) Info() (Info, error) {
26+
if c.err != nil {
27+
return Info{}, c.err
28+
}
29+
return c.info, nil
30+
}
31+
32+
func (c *crioClientMock) ContainerInfo(id string) (*ContainerInfo, error) {
33+
if c.err != nil {
34+
return nil, c.err
35+
}
36+
cInfo, ok := c.containersInfo[id]
37+
if !ok {
38+
return nil, fmt.Errorf("no container with id %s", id)
39+
}
40+
return cInfo, nil
41+
}
42+
43+
func mockCrioClient(info Info, containersInfo map[string]*ContainerInfo, err error) crioClient {
44+
return &crioClientMock{
45+
err: err,
46+
info: info,
47+
containersInfo: containersInfo,
48+
}
49+
}

Diff for: 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)