Artem Chernyshev 76aac4bb25 feat: implement CPU and Memory stats controller
Now the latest value for CPU and Memory is also represented as COSI
resources.

Was going back and forth in the implementation but in the end decided to
use dedicated yaml structures for both CPU and Memory stats because:

- JSON tags are ignored by `go-yaml`, so the output is not really great.
- protobuf Talos definition contains fields which we don't really need
in the YAML output of `talosctl get`.
- current state of Talos resource service does not support protobuf
encoding for resources.

So the plan for Theila is to just use the structure as a dynamic object
without relying on protobufs. At least for now.

Signed-off-by: Artem Chernyshev <artem.0xD2@gmail.com>
2021-06-03 10:19:27 -07:00

111 lines
2.3 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package perf
import (
"context"
"time"
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/prometheus/procfs"
"go.uber.org/zap"
"github.com/talos-systems/talos/pkg/resources/perf"
)
const updateInterval = time.Second * 30
// StatsController manages v1alpha1.Stats which is the current snaphot of the machine CPU and Memory consumption.
type StatsController struct{}
// Name implements controller.StatsController interface.
func (ctrl *StatsController) Name() string {
return "perf.StatsController"
}
// Inputs implements controller.StatsController interface.
func (ctrl *StatsController) Inputs() []controller.Input {
return nil
}
// Outputs implements controller.StatsController interface.
func (ctrl *StatsController) Outputs() []controller.Output {
return []controller.Output{
{
Type: perf.CPUType,
Kind: controller.OutputExclusive,
},
{
Type: perf.MemoryType,
Kind: controller.OutputExclusive,
},
}
}
// Run implements controller.StatsController interface.
func (ctrl *StatsController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
ticker := time.NewTicker(updateInterval)
defer ticker.Stop()
var (
fs procfs.FS
err error
)
fs, err = procfs.NewDefaultFS()
if err != nil {
return err
}
for {
select {
case <-r.EventCh():
case <-ctx.Done():
return nil
case <-ticker.C:
}
if err := ctrl.updateMemory(ctx, r, &fs); err != nil {
return err
}
if err := ctrl.updateCPU(ctx, r, &fs); err != nil {
return err
}
}
}
func (ctrl *StatsController) updateCPU(ctx context.Context, r controller.Runtime, fs *procfs.FS) error {
cpu := perf.NewCPU()
stat, err := fs.Stat()
if err != nil {
return err
}
return r.Modify(ctx, cpu, func(r resource.Resource) error {
r.(*perf.CPU).Update(&stat)
return nil
})
}
func (ctrl *StatsController) updateMemory(ctx context.Context, r controller.Runtime, fs *procfs.FS) error {
mem := perf.NewMemory()
info, err := fs.Meminfo()
if err != nil {
return err
}
return r.Modify(ctx, mem, func(r resource.Resource) error {
r.(*perf.Memory).Update(&info)
return nil
})
}