feat(agent)!: add support for mTLS authentication in gRPC server (#54)

* refactor(fancontroller): improve fan controller validation logic and error handling for temperature steps

* refactor(agent): restructure gRPC server implementation by moving it to a new api package for better organization and maintainability

* feat(agent): implement gRPC server for managing compute blade agents and add graceful shutdown support
refactor(agent): restructure agent code by moving API logic to a dedicated file and improving error handling
fix(agent): update logging messages for clarity and consistency across the agent's operations
chore(agent): remove unused API code and consolidate event handling logic for better maintainability
style(agent): improve code formatting and organization for better readability and adherence to conventions

* feat(agent): add support for TLS configuration in gRPC server

* feat(api): add gRPC server authentication

* fix

* feat(config): add listen mode configuration to support tcp or unix sockets
feat(agent): implement listen mode in gRPC service to allow flexible socket types
feat(bladectl): enhance configuration loading and add support for TLS credentials
fix(bladectl): improve error handling for gRPC connection and event emission
style(logging): change log level from Warn to Info for better clarity in logs

* add logging middleware + fixes

* fix remote-connection to gRPC API Server

debugging the SAN issues took the soul out of me... And then the stupid
mistake in cmd_root where I didn't construct the TLS credentials
correctly... Oh dear...

* cleanup

* cleanup

* cleanup commands

* cleanup

* make README.md nicer

* Update cmd/agent/main.go

Co-authored-by: Matthias Riegler <github@m4tbit.de>

* Update cmd/bladectl/cmd_root.go

Co-authored-by: Matthias Riegler <github@m4tbit.de>

* move bladectl config into correct directory

* fix bugs

* // FIXME: No dead code

* nit: code style

* nit(YAGNI): you aint gonna need it. Don't make life harder than it needs to be

* nit(YAGNI): you aint gonna need it. Don't make life harder than it needs to be

* nit(YAGNI): you aint gonna need it. Don't make life harder than it needs to be

* nit(cmd_identify)

---------

Co-authored-by: Matthias Riegler <github@m4tbit.de>
This commit is contained in:
Cedric Kienzler
2025-05-12 00:00:55 +02:00
committed by GitHub
parent ec6229ad86
commit 70541d86ba
60 changed files with 2189 additions and 650 deletions

View File

@@ -3,19 +3,29 @@ package smartfanunit
import (
"errors"
"github.com/uptime-induestries/compute-blade-agent/pkg/hal/led"
"github.com/uptime-induestries/compute-blade-agent/pkg/smartfanunit/proto"
"github.com/uptime-industries/compute-blade-agent/pkg/hal/led"
"github.com/uptime-industries/compute-blade-agent/pkg/smartfanunit/proto"
)
// Blade -> FanUnit communication
const (
// Blade -> FanUnit
// CmdSetFanSpeedPercent sets the fan speed as a percentage, sent from the blade to the fan unit.
CmdSetFanSpeedPercent proto.Command = 0x01
CmdSetLED proto.Command = 0x02
// FanUnit -> Blade, sent in regular intervals
NotifyButtonPress proto.Command = 0xa1
// CmdSetLED represents the command to set the LED color, sent from the blade to the fan unit.
CmdSetLED proto.Command = 0x02
)
// FanUnit -> Blade, sent in regular intervals
const (
// NotifyButtonPress represents a command sent from the fan unit to indicate a button press event.
NotifyButtonPress proto.Command = 0xa1
// NotifyAirFlowTemperature represents a command sent from the fan unit to report the current air flow temperature.
NotifyAirFlowTemperature proto.Command = 0xa2
NotifyFanSpeedRPM proto.Command = 0xa3
// NotifyFanSpeedRPM is a command used to report the current fan speed in RPM from the fan unit to the blade.
NotifyFanSpeedRPM proto.Command = 0xa3
)
var ErrInvalidCommand = errors.New("invalid command")
@@ -93,7 +103,7 @@ type AirFlowTemperaturePacket struct {
func (p *AirFlowTemperaturePacket) Packet() proto.Packet {
return proto.Packet{
Command: NotifyAirFlowTemperature,
Data: proto.Data(float32To24Bit(p.Temperature)),
Data: float32To24Bit(p.Temperature),
}
}

View File

@@ -1,4 +1,4 @@
// This is a driver for the EMC2101 fan controller
// Package emc2101 is a driver for the EMC2101 fan controller
// Based on https://ww1.microchip.com/downloads/en/DeviceDoc/2101.pdf
package emc2101
@@ -57,7 +57,6 @@ func (e *emc2101) updateReg(regAddr, setMask, clearMask uint8) error {
return nil
}
return e.bus.Tx(e.Address, []byte{regAddr, toWrite}, nil)
}
@@ -74,10 +73,10 @@ func (e *emc2101) Init() error {
}
/*
0x3 0b100
0x4b 0b11111
0x4a 0b100000
0x4a 0b100000
0x3 0b100
0x4b 0b11111
0x4a 0b100000
0x4a 0b100000
*/
// Configure fan spin up to ignore tach input
@@ -126,7 +125,7 @@ func (e *emc2101) FanRPM() (float32, error) {
return 0, err
}
var tachCount int = int(high[0])<<8 | int(low[0])
var tachCount = int(high[0])<<8 | int(low[0])
return float32(5400000) / float32(tachCount), nil
}

View File

@@ -1,6 +1,6 @@
package smartfanunit
import "github.com/uptime-induestries/compute-blade-agent/pkg/smartfanunit/proto"
import "github.com/uptime-industries/compute-blade-agent/pkg/smartfanunit/proto"
func float32To24Bit(val float32) proto.Data {
// Convert float32 to number with 3 bytes (0.1 precision)

View File

@@ -78,7 +78,7 @@ func WritePacket(_ context.Context, w io.Writer, packet Packet) error {
// ReadPacket reads a packet from an io.Reader with escaping.
// This is blocking and drops invalid bytes until a valid packet is received.
func ReadPacket(ctx context.Context, r io.Reader) (Packet, error) {
buffer := []uint8{}
var buffer []uint8
started := false
escaped := false

View File

@@ -6,7 +6,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/uptime-induestries/compute-blade-agent/pkg/smartfanunit/proto"
"github.com/uptime-industries/compute-blade-agent/pkg/smartfanunit/proto"
)
func TestWritePacket(t *testing.T) {

View File

@@ -1,11 +1,11 @@
package smartfanunit
import (
"github.com/uptime-induestries/compute-blade-agent/pkg/smartfanunit/proto"
"github.com/uptime-industries/compute-blade-agent/pkg/smartfanunit/proto"
)
const (
Baudrate = 115200
BaudRate = 115200
)
func MatchCmd(cmd proto.Command) func(any) bool {