2022-10-21 10:42:36 +02:00

135 lines
3.1 KiB
Go

// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License-AGPL.txt in the project root for license information.
package cgroup
import (
"context"
"github.com/gitpod-io/gitpod/common-go/cgroups"
"github.com/gitpod-io/gitpod/common-go/log"
"github.com/gitpod-io/gitpod/ws-daemon/pkg/dispatch"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/xerrors"
)
func NewPluginHost(cgroupBasePath string, plugins ...Plugin) (*PluginHost, error) {
var version Version
unified, err := cgroups.IsUnifiedCgroupSetup()
if err != nil {
return nil, xerrors.Errorf("could not determine cgroup setup: %w", err)
}
if unified {
version = Version2
} else {
version = Version1
}
return &PluginHost{
CGroupBasePath: cgroupBasePath,
CGroupVersion: version,
Plugins: plugins,
pluginActivationTotalVec: prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "cgroup_plugin_activation_total",
Help: "counts the total activation of cgroup plugins",
}, []string{"plugin", "success"}),
}, nil
}
type PluginHost struct {
CGroupBasePath string
CGroupVersion Version
Plugins []Plugin
pluginActivationTotalVec *prometheus.CounterVec
}
var _ dispatch.Listener = &PluginHost{}
var _ prometheus.Collector = &PluginHost{}
func (host *PluginHost) Describe(c chan<- *prometheus.Desc) {
host.pluginActivationTotalVec.Describe(c)
for _, p := range host.Plugins {
col, ok := p.(prometheus.Collector)
if !ok {
continue
}
col.Describe(c)
}
}
func (host *PluginHost) Collect(c chan<- prometheus.Metric) {
host.pluginActivationTotalVec.Collect(c)
for _, p := range host.Plugins {
col, ok := p.(prometheus.Collector)
if !ok {
continue
}
col.Collect(c)
}
}
func (host *PluginHost) WorkspaceAdded(ctx context.Context, ws *dispatch.Workspace) (err error) {
disp := dispatch.GetFromContext(ctx)
if disp == nil {
return xerrors.Errorf("no dispatch available")
}
cgroupPath, err := disp.Runtime.ContainerCGroupPath(context.Background(), ws.ContainerID)
if err != nil {
return xerrors.Errorf("cannot get cgroup path for container %s: %w", ws.ContainerID, err)
}
opts := &PluginOptions{
BasePath: host.CGroupBasePath,
CgroupPath: cgroupPath,
InstanceId: ws.InstanceID,
Annotations: ws.Pod.Annotations,
}
for _, plg := range host.Plugins {
if plg.Type() != host.CGroupVersion {
continue
}
go func(plg Plugin) {
err := plg.Apply(ctx, opts)
if err == context.Canceled || err == context.DeadlineExceeded {
err = nil
}
if err != nil {
log.WithError(err).WithFields(ws.OWI()).WithField("plugin", plg.Name()).Error("cgroup plugin failure")
host.pluginActivationTotalVec.WithLabelValues(plg.Name(), "false").Inc()
} else {
host.pluginActivationTotalVec.WithLabelValues(plg.Name(), "true").Inc()
}
}(plg)
}
return nil
}
type Plugin interface {
Name() string
Type() Version
Apply(ctx context.Context, options *PluginOptions) error
}
type Version int
const (
Version1 Version = iota
Version2
)
type PluginOptions struct {
BasePath string
CgroupPath string
InstanceId string
Annotations map[string]string
}