chore: some improvements in bladectl sub-command handling, error logging, and CI (#51)

chore(ci): update Go setup action to v5 and simplify caching configuration for improved performance
chore(release): update Go setup action to v5 and simplify caching configuration for improved performance
fix(.gitignore): add .idea directory to ignore list to prevent IDE files from being tracked
feat(goreleaser): add versioning information to builds for better traceability
feat(agent): expose version, commit, and date information in logs for better tracking
feat(bladectl): implement command structure for managing compute-blade features
fix(bladectl): improve error handling in identify command for better user feedback
chore(go.mod): update dependencies to latest versions for improved stability and features
This commit is contained in:
Cedric Kienzler
2025-05-03 11:13:37 +02:00
committed by GitHub
parent c5ff21d522
commit ec6229ad86
13 changed files with 162 additions and 116 deletions

View File

@@ -22,6 +22,12 @@ import (
"google.golang.org/grpc"
)
var (
Version string
Commit string
Date string
)
func main() {
var wg sync.WaitGroup
@@ -84,7 +90,7 @@ func main() {
}
}()
log.FromContext(ctx).Info("Bootstrapping compute-blade-agent", zap.String("version", viper.GetString("version")))
log.FromContext(ctx).Info("Bootstrapping compute-blade-agent", zap.String("version", Version), zap.String("commit", Commit), zap.String("date", Date))
computebladeAgent, err := agent.NewComputeBladeAgent(ctx, cbAgentConfig)
if err != nil {
log.FromContext(ctx).Error("Failed to create agent", zap.Error(err))

View File

@@ -1,40 +1,33 @@
package main
import (
"strconv"
"github.com/spf13/cobra"
bladeapiv1alpha1 "github.com/uptime-induestries/compute-blade-agent/api/bladeapi/v1alpha1"
)
var (
percent int
)
func init() {
cmdFan.AddCommand(cmdFanSetPercent)
rootCmd.AddCommand(cmdFan)
cmdFan.Flags().IntVarP(&percent, "percent", "p", 40, "Fan speed in percent (Default: 40).")
_ = cmdFan.MarkFlagRequired("percent")
cmdSet.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),
Use: "fan",
Short: "Control the fan behavior of the compute-blade",
Example: "bladectl set fan --percent 50",
Args: cobra.ExactArgs(0),
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),
})

View File

@@ -1,21 +1,28 @@
package main
import (
"github.com/sierrasoftworks/humane-errors-go"
"github.com/spf13/cobra"
bladeapiv1alpha1 "github.com/uptime-induestries/compute-blade-agent/api/bladeapi/v1alpha1"
"google.golang.org/protobuf/types/known/emptypb"
)
var (
confirm bool
wait bool
)
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)
cmdIdentify.Flags().BoolVarP(&confirm, "confirm", "c", false, "confirm the identify state")
cmdIdentify.Flags().BoolVarP(&wait, "wait", "w", false, "Wait for the identify state to be confirmed (e.g. by a physical button press)")
cmdSet.AddCommand(cmdIdentify)
}
var cmdIdentify = &cobra.Command{
Use: "identify",
Short: "interact with the compute-blade identity LED",
RunE: runIdentity,
Use: "identify",
Example: "bladectl set identify --wait",
Short: "interact with the compute-blade identity LED",
RunE: runIdentity,
}
func runIdentity(cmd *cobra.Command, _ []string) error {
@@ -24,16 +31,6 @@ func runIdentity(cmd *cobra.Command, _ []string) 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 {
@@ -43,14 +40,14 @@ func runIdentity(cmd *cobra.Command, _ []string) error {
// Emit the event to the compute-blade-agent
_, err = client.EmitEvent(ctx, &bladeapiv1alpha1.EmitEventRequest{Event: event})
if err != nil {
return err
return humane.Wrap(err, "failed to emit event", "ensure the compute-blade agent is running and responsive to requests", "check the compute-blade agent logs for more information using 'journalctl -u compute-blade-agent.service'")
}
// 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 humane.Wrap(err, "unable to wait for confirmation", "ensure the compute-blade agent is running and responsive to requests", "check the compute-blade agent logs for more information using 'journalctl -u compute-blade-agent.service'")
}
}

46
cmd/bladectl/cmd_root.go Normal file
View File

@@ -0,0 +1,46 @@
package main
import (
"context"
humane "github.com/sierrasoftworks/humane-errors-go"
"github.com/spf13/cobra"
bladeapiv1alpha1 "github.com/uptime-induestries/compute-blade-agent/api/bladeapi/v1alpha1"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"os"
"os/signal"
"syscall"
)
var rootCmd = &cobra.Command{
Use: "bladectl",
Short: "bladectl interacts with the compute-blade-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()
}
}()
conn, err := grpc.Dial(grpcAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return humane.Wrap(err, "failed to dial grpc server", "ensure the gRPC server you are trying to connect to is running and the address is correct")
}
client := bladeapiv1alpha1.NewBladeAgentServiceClient(conn)
cmd.SetContext(clientIntoContext(ctx, client))
return nil
},
}

24
cmd/bladectl/cmd_verbs.go Normal file
View File

@@ -0,0 +1,24 @@
package main
import (
"github.com/spf13/cobra"
)
func init() {
rootCmd.AddCommand(cmdGet)
rootCmd.AddCommand(cmdSet)
}
var (
cmdGet = &cobra.Command{
Use: "get",
Short: "Display compute-blade related information",
Long: "Prints information about compute-blade related information, e.g. fan speed, temperature, etc.",
}
cmdSet = &cobra.Command{
Use: "set",
Short: "Configure compute-blade",
Long: "These commands allow you make changes to compute-blade related information.",
}
)

View File

@@ -0,0 +1,22 @@
package main
import (
"fmt"
"github.com/spf13/cobra"
)
func init() {
rootCmd.AddCommand(cmdVersion)
}
var cmdVersion = &cobra.Command{
Use: "version",
Short: "Shows version information",
Example: "bladectl version",
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Version: %s\n", Version)
fmt.Printf("Date: %s\n", Date)
fmt.Printf("Commit: %s\n", Commit)
},
}

View File

@@ -2,17 +2,10 @@ package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"syscall"
"time"
"github.com/spf13/cobra"
bladeapiv1alpha1 "github.com/uptime-induestries/compute-blade-agent/api/bladeapi/v1alpha1"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
type grpcClientContextKey int
@@ -25,6 +18,10 @@ const (
var (
grpcAddr string
timeout time.Duration
Version string
Commit string
Date string
)
func init() {
@@ -45,39 +42,6 @@ func clientFromContext(ctx context.Context) bladeapiv1alpha1.BladeAgentServiceCl
return client
}
var rootCmd = &cobra.Command{
Use: "bladectl",
Short: "bladectl interacts with the compute-blade-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()
}
}()
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)