1// +build linux
2
3/*
4Copyright 2015 The Kubernetes Authors.
5
6Licensed under the Apache License, Version 2.0 (the "License");
7you may not use this file except in compliance with the License.
8You may obtain a copy of the License at
9
10    http://www.apache.org/licenses/LICENSE-2.0
11
12Unless required by applicable law or agreed to in writing, software
13distributed under the License is distributed on an "AS IS" BASIS,
14WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15See the License for the specific language governing permissions and
16limitations under the License.
17*/
18
19package cadvisor
20
21import (
22	"flag"
23	"fmt"
24	"net/http"
25	"os"
26	"path"
27	"time"
28
29	// Register supported container handlers.
30	_ "github.com/google/cadvisor/container/containerd/install"
31	_ "github.com/google/cadvisor/container/crio/install"
32	_ "github.com/google/cadvisor/container/systemd/install"
33
34	"github.com/google/cadvisor/cache/memory"
35	cadvisormetrics "github.com/google/cadvisor/container"
36	"github.com/google/cadvisor/events"
37	cadvisorapi "github.com/google/cadvisor/info/v1"
38	cadvisorapiv2 "github.com/google/cadvisor/info/v2"
39	"github.com/google/cadvisor/manager"
40	"github.com/google/cadvisor/utils/sysfs"
41	utilfeature "k8s.io/apiserver/pkg/util/feature"
42	"k8s.io/klog/v2"
43	kubefeatures "k8s.io/kubernetes/pkg/features"
44	"k8s.io/utils/pointer"
45)
46
47type cadvisorClient struct {
48	imageFsInfoProvider ImageFsInfoProvider
49	rootPath            string
50	manager.Manager
51}
52
53var _ Interface = new(cadvisorClient)
54
55// TODO(vmarmol): Make configurable.
56// The amount of time for which to keep stats in memory.
57const statsCacheDuration = 2 * time.Minute
58const maxHousekeepingInterval = 15 * time.Second
59const defaultHousekeepingInterval = 10 * time.Second
60const allowDynamicHousekeeping = true
61
62func init() {
63	// Override cAdvisor flag defaults.
64	flagOverrides := map[string]string{
65		// Override the default cAdvisor housekeeping interval.
66		"housekeeping_interval": defaultHousekeepingInterval.String(),
67		// Disable event storage by default.
68		"event_storage_event_limit": "default=0",
69		"event_storage_age_limit":   "default=0",
70	}
71	for name, defaultValue := range flagOverrides {
72		if f := flag.Lookup(name); f != nil {
73			f.DefValue = defaultValue
74			f.Value.Set(defaultValue)
75		} else {
76			klog.ErrorS(nil, "Expected cAdvisor flag not found", "flag", name)
77		}
78	}
79}
80
81// New creates a new cAdvisor Interface for linux systems.
82func New(imageFsInfoProvider ImageFsInfoProvider, rootPath string, cgroupRoots []string, usingLegacyStats bool) (Interface, error) {
83	sysFs := sysfs.NewRealSysFs()
84
85	includedMetrics := cadvisormetrics.MetricSet{
86		cadvisormetrics.CpuUsageMetrics:     struct{}{},
87		cadvisormetrics.MemoryUsageMetrics:  struct{}{},
88		cadvisormetrics.CpuLoadMetrics:      struct{}{},
89		cadvisormetrics.DiskIOMetrics:       struct{}{},
90		cadvisormetrics.NetworkUsageMetrics: struct{}{},
91		cadvisormetrics.AppMetrics:          struct{}{},
92		cadvisormetrics.ProcessMetrics:      struct{}{},
93	}
94
95	// Only add the Accelerator metrics if the feature is inactive
96	if !utilfeature.DefaultFeatureGate.Enabled(kubefeatures.DisableAcceleratorUsageMetrics) {
97		includedMetrics[cadvisormetrics.AcceleratorUsageMetrics] = struct{}{}
98	}
99
100	if usingLegacyStats || utilfeature.DefaultFeatureGate.Enabled(kubefeatures.LocalStorageCapacityIsolation) {
101		includedMetrics[cadvisormetrics.DiskUsageMetrics] = struct{}{}
102	}
103
104	duration := maxHousekeepingInterval
105	housekeepingConfig := manager.HouskeepingConfig{
106		Interval:     &duration,
107		AllowDynamic: pointer.BoolPtr(allowDynamicHousekeeping),
108	}
109
110	// Create the cAdvisor container manager.
111	m, err := manager.New(memory.New(statsCacheDuration, nil), sysFs, housekeepingConfig, includedMetrics, http.DefaultClient, cgroupRoots, "")
112	if err != nil {
113		return nil, err
114	}
115
116	if _, err := os.Stat(rootPath); err != nil {
117		if os.IsNotExist(err) {
118			if err := os.MkdirAll(path.Clean(rootPath), 0750); err != nil {
119				return nil, fmt.Errorf("error creating root directory %q: %v", rootPath, err)
120			}
121		} else {
122			return nil, fmt.Errorf("failed to Stat %q: %v", rootPath, err)
123		}
124	}
125
126	return &cadvisorClient{
127		imageFsInfoProvider: imageFsInfoProvider,
128		rootPath:            rootPath,
129		Manager:             m,
130	}, nil
131}
132
133func (cc *cadvisorClient) Start() error {
134	return cc.Manager.Start()
135}
136
137func (cc *cadvisorClient) ContainerInfo(name string, req *cadvisorapi.ContainerInfoRequest) (*cadvisorapi.ContainerInfo, error) {
138	return cc.GetContainerInfo(name, req)
139}
140
141func (cc *cadvisorClient) ContainerInfoV2(name string, options cadvisorapiv2.RequestOptions) (map[string]cadvisorapiv2.ContainerInfo, error) {
142	return cc.GetContainerInfoV2(name, options)
143}
144
145func (cc *cadvisorClient) VersionInfo() (*cadvisorapi.VersionInfo, error) {
146	return cc.GetVersionInfo()
147}
148
149func (cc *cadvisorClient) SubcontainerInfo(name string, req *cadvisorapi.ContainerInfoRequest) (map[string]*cadvisorapi.ContainerInfo, error) {
150	infos, err := cc.SubcontainersInfo(name, req)
151	if err != nil && len(infos) == 0 {
152		return nil, err
153	}
154
155	result := make(map[string]*cadvisorapi.ContainerInfo, len(infos))
156	for _, info := range infos {
157		result[info.Name] = info
158	}
159	return result, err
160}
161
162func (cc *cadvisorClient) MachineInfo() (*cadvisorapi.MachineInfo, error) {
163	return cc.GetMachineInfo()
164}
165
166func (cc *cadvisorClient) ImagesFsInfo() (cadvisorapiv2.FsInfo, error) {
167	label, err := cc.imageFsInfoProvider.ImageFsInfoLabel()
168	if err != nil {
169		return cadvisorapiv2.FsInfo{}, err
170	}
171	return cc.getFsInfo(label)
172}
173
174func (cc *cadvisorClient) RootFsInfo() (cadvisorapiv2.FsInfo, error) {
175	return cc.GetDirFsInfo(cc.rootPath)
176}
177
178func (cc *cadvisorClient) getFsInfo(label string) (cadvisorapiv2.FsInfo, error) {
179	res, err := cc.GetFsInfo(label)
180	if err != nil {
181		return cadvisorapiv2.FsInfo{}, err
182	}
183	if len(res) == 0 {
184		return cadvisorapiv2.FsInfo{}, fmt.Errorf("failed to find information for the filesystem labeled %q", label)
185	}
186	// TODO(vmarmol): Handle this better when a label has more than one image filesystem.
187	if len(res) > 1 {
188		klog.InfoS("More than one filesystem labeled. Only using the first one", "label", label, "fileSystem", res)
189	}
190
191	return res[0], nil
192}
193
194func (cc *cadvisorClient) WatchEvents(request *events.Request) (*events.EventChannel, error) {
195	return cc.WatchForEvents(request)
196}
197