mirror of
https://github.com/compute-blade-community/compute-blade-agent.git
synced 2026-04-16 15:35:42 +02:00
feat: add rudimentary API & bladectl client
Signed-off-by: Matthias Riegler <matthias.riegler@ankorstore.com>
This commit is contained in:
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
@@ -11,10 +12,12 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
bladeapiv1alpha1 "github.com/xvzf/computeblade-agent/api/bladeapi/v1alpha1"
|
||||
"github.com/xvzf/computeblade-agent/internal/agent"
|
||||
"github.com/xvzf/computeblade-agent/pkg/ledengine"
|
||||
"github.com/xvzf/computeblade-agent/pkg/log"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -29,7 +32,7 @@ func main() {
|
||||
ctx, cancelCtx := context.WithCancelCause(baseCtx)
|
||||
defer cancelCtx(context.Canceled)
|
||||
|
||||
agent, err := agent.NewComputeBladeAgent(agent.ComputeBladeAgentConfig{
|
||||
computebladeAgent, err := agent.NewComputeBladeAgent(agent.ComputeBladeAgentConfig{
|
||||
IdleLedColor: ledengine.LedColorGreen(0.05),
|
||||
IdentifyLedColor: ledengine.LedColorPurple(0.05),
|
||||
CriticalLedColor: ledengine.LedColorRed(0.3),
|
||||
@@ -61,13 +64,41 @@ func main() {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
err := agent.Run(ctx)
|
||||
err := computebladeAgent.Run(ctx)
|
||||
if err != nil && err != context.Canceled {
|
||||
log.FromContext(ctx).Error("Failed to run agent", zap.Error(err))
|
||||
cancelCtx(err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Setup GRPC server
|
||||
// FIXME add logging middleware
|
||||
grpcServer := grpc.NewServer()
|
||||
bladeapiv1alpha1.RegisterBladeAgentServiceServer(grpcServer, agent.NewGrpcServiceFor(computebladeAgent))
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
socketPath := "/tmp/computeblade-agent.sock"
|
||||
grpcListen, err := net.Listen("unix", "/tmp/computeblade-agent.sock")
|
||||
if err != nil {
|
||||
log.FromContext(ctx).Error("Failed to create grpc listener", zap.Error(err))
|
||||
cancelCtx(err)
|
||||
return
|
||||
}
|
||||
log.FromContext(ctx).Info("Starting grpc server", zap.String("address", socketPath))
|
||||
if err := grpcServer.Serve(grpcListen); err != nil && err != grpc.ErrServerStopped {
|
||||
log.FromContext(ctx).Error("Failed to start grpc server", zap.Error(err))
|
||||
cancelCtx(err)
|
||||
}
|
||||
}()
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
<-ctx.Done()
|
||||
log.FromContext(ctx).Info("Shutting down grpc server")
|
||||
grpcServer.GracefulStop()
|
||||
}()
|
||||
|
||||
// setup prometheus endpoint
|
||||
promHandler := http.NewServeMux()
|
||||
promHandler.Handle("/metrics", promhttp.Handler())
|
||||
|
||||
45
cmd/bladectl/cmd_fan.go
Normal file
45
cmd/bladectl/cmd_fan.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
bladeapiv1alpha1 "github.com/xvzf/computeblade-agent/api/bladeapi/v1alpha1"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmdFan.AddCommand(cmdFanSetPercent)
|
||||
rootCmd.AddCommand(cmdFan)
|
||||
}
|
||||
|
||||
var (
|
||||
cmdFan = &cobra.Command{
|
||||
Use: "fan",
|
||||
Short: "Fan-related commands for the compute blade",
|
||||
}
|
||||
|
||||
cmdFanSetPercent = &cobra.Command{
|
||||
Use: "set-percent <percent>",
|
||||
Example: "bladectl fan set-percent 50",
|
||||
Short: "Set the fan speed in percent",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
|
||||
ctx := cmd.Context()
|
||||
client := clientFromContext(ctx)
|
||||
|
||||
// convert string to int
|
||||
percent, err := strconv.Atoi(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = client.SetFanSpeed(ctx, &bladeapiv1alpha1.SetFanSpeedRequest{
|
||||
Percent: int64(percent),
|
||||
})
|
||||
|
||||
return err
|
||||
},
|
||||
}
|
||||
)
|
||||
58
cmd/bladectl/cmd_identify.go
Normal file
58
cmd/bladectl/cmd_identify.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
bladeapiv1alpha1 "github.com/xvzf/computeblade-agent/api/bladeapi/v1alpha1"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmdIdentify.Flags().Bool("confirm", false, "confirm the identify state")
|
||||
cmdIdentify.Flags().Bool("wait", false, "Wait for the identify state to be confirmed (e.g. by a physical button press)")
|
||||
rootCmd.AddCommand(cmdIdentify)
|
||||
}
|
||||
|
||||
var cmdIdentify = &cobra.Command{
|
||||
Use: "identify",
|
||||
Short: "interact with the compute-blade identity LED",
|
||||
RunE: runIdentity,
|
||||
}
|
||||
|
||||
func runIdentity(cmd *cobra.Command, _ []string) error {
|
||||
var err error
|
||||
|
||||
ctx := cmd.Context()
|
||||
client := clientFromContext(ctx)
|
||||
|
||||
// Get flags
|
||||
confirm, err := cmd.Flags().GetBool("confirm")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wait, err := cmd.Flags().GetBool("wait")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if we should wait for the identify state to be confirmed
|
||||
event := bladeapiv1alpha1.Event_IDENTIFY
|
||||
if confirm {
|
||||
event = bladeapiv1alpha1.Event_IDENTIFY_CONFIRM
|
||||
}
|
||||
|
||||
// Emit the event to the computeblade-agent
|
||||
_, err = client.EmitEvent(ctx, &bladeapiv1alpha1.EmitEventRequest{Event: event})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if we should wait for the identify state to be confirmed
|
||||
if wait {
|
||||
_, err := client.WaitForIdentifyConfirm(ctx, &emptypb.Empty{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
85
cmd/bladectl/main.go
Normal file
85
cmd/bladectl/main.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
bladeapiv1alpha1 "github.com/xvzf/computeblade-agent/api/bladeapi/v1alpha1"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
)
|
||||
|
||||
type grpcClientContextKey int
|
||||
|
||||
const defaultGrpcClientContextKey grpcClientContextKey = 0
|
||||
|
||||
var (
|
||||
grpcAddr string
|
||||
timeout time.Duration
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().
|
||||
StringVar(&grpcAddr, "addr", "unix:///tmp/computeblade-agent.sock", "address of the computeblade-agent gRPC server")
|
||||
rootCmd.PersistentFlags().DurationVar(&timeout, "timeout", time.Minute, "timeout for gRPC requests")
|
||||
}
|
||||
|
||||
func clientIntoContext(ctx context.Context, client bladeapiv1alpha1.BladeAgentServiceClient) context.Context {
|
||||
return context.WithValue(ctx, defaultGrpcClientContextKey, client)
|
||||
}
|
||||
|
||||
func clientFromContext(ctx context.Context) (bladeapiv1alpha1.BladeAgentServiceClient) {
|
||||
client, ok := ctx.Value(defaultGrpcClientContextKey).(bladeapiv1alpha1.BladeAgentServiceClient)
|
||||
if !ok {
|
||||
panic("grpc client not found in context")
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "bladectl",
|
||||
Short: "bladectl interacts with the computeblade-agent and allows you to manage hardware-features of your compute blade(s)",
|
||||
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
|
||||
origCtx := cmd.Context()
|
||||
|
||||
// setup signal handlers for SIGINT and SIGTERM
|
||||
ctx, cancelCtx := context.WithTimeout(origCtx, timeout)
|
||||
|
||||
// setup signal handler channels
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
go func() {
|
||||
// Wait for context cancel or signal
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-sigs:
|
||||
// On signal, cancel context
|
||||
cancelCtx()
|
||||
}
|
||||
}()
|
||||
|
||||
// FIXME handle conn teardown properly
|
||||
// setup grpc client
|
||||
conn, err := grpc.Dial(grpcAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to dial grpc server: %w", err)
|
||||
}
|
||||
client := bladeapiv1alpha1.NewBladeAgentServiceClient(conn)
|
||||
|
||||
cmd.SetContext(clientIntoContext(ctx, client))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user