mirror of
https://github.com/apricote/hcloud-upload-image.git
synced 2025-08-10 07:46:57 +02:00
The current setup of the CLI requires the user to set HCLOUD_TOKEN for every single invocation of the binary. Even if we just want to autocomplete some arguments or even generate the completion scripts in CI. This fixes the bug by only initializing the hcloud-go client in the "cleanup" and "upload" subcommands.
117 lines
2.8 KiB
Go
117 lines
2.8 KiB
Go
package cmd
|
|
|
|
import (
|
|
"log/slog"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/hetznercloud/hcloud-go/v2/hcloud"
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/apricote/hcloud-upload-image/hcloudimages"
|
|
"github.com/apricote/hcloud-upload-image/hcloudimages/backoff"
|
|
"github.com/apricote/hcloud-upload-image/hcloudimages/contextlogger"
|
|
"github.com/apricote/hcloud-upload-image/internal/ui"
|
|
"github.com/apricote/hcloud-upload-image/internal/version"
|
|
)
|
|
|
|
const (
|
|
flagVerbose = "verbose"
|
|
)
|
|
|
|
var (
|
|
// 1 activates slog debug output
|
|
// 2 activates hcloud-go debug output
|
|
verbose int
|
|
)
|
|
|
|
// The pre-authenticated client. Set in the root command PersistentPreRun
|
|
var client *hcloudimages.Client
|
|
|
|
// rootCmd represents the base command when called without any subcommands
|
|
var rootCmd = &cobra.Command{
|
|
Use: "hcloud-upload-image",
|
|
Long: `Manage custom OS images on Hetzner Cloud.`,
|
|
SilenceUsage: true,
|
|
|
|
Version: version.Version,
|
|
|
|
PersistentPreRun: func(cmd *cobra.Command, _ []string) {
|
|
ctx := cmd.Context()
|
|
|
|
slog.SetDefault(initLogger())
|
|
|
|
// Add logger to command context
|
|
logger := slog.Default()
|
|
ctx = contextlogger.New(ctx, logger)
|
|
cmd.SetContext(ctx)
|
|
},
|
|
}
|
|
|
|
func initLogger() *slog.Logger {
|
|
logLevel := slog.LevelInfo
|
|
if verbose >= 1 {
|
|
logLevel = slog.LevelDebug
|
|
}
|
|
|
|
return slog.New(ui.NewHandler(os.Stdout, &ui.HandlerOptions{
|
|
Level: logLevel,
|
|
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
|
|
// Remove attributes that are unnecessary for the cli context
|
|
if a.Key == "library" || a.Key == "method" {
|
|
return slog.Attr{}
|
|
}
|
|
|
|
return a
|
|
},
|
|
}))
|
|
|
|
}
|
|
|
|
func initClient(cmd *cobra.Command, _ []string) {
|
|
if client != nil {
|
|
// Only init if not set.
|
|
// Theoretically this is not safe against data races and should use [sync.Once], but :shrug:
|
|
return
|
|
}
|
|
|
|
ctx := cmd.Context()
|
|
|
|
logger := contextlogger.From(ctx)
|
|
// Build hcloud-go client
|
|
if os.Getenv("HCLOUD_TOKEN") == "" {
|
|
logger.ErrorContext(ctx, "You need to set the HCLOUD_TOKEN environment variable to your Hetzner Cloud API Token.")
|
|
os.Exit(1)
|
|
}
|
|
|
|
opts := []hcloud.ClientOption{
|
|
hcloud.WithToken(os.Getenv("HCLOUD_TOKEN")),
|
|
hcloud.WithApplication("hcloud-upload-image", version.Version),
|
|
hcloud.WithPollBackoffFunc(backoff.ExponentialBackoffWithLimit(2, 1*time.Second, 30*time.Second)),
|
|
}
|
|
|
|
if os.Getenv("HCLOUD_DEBUG") != "" || verbose >= 2 {
|
|
opts = append(opts, hcloud.WithDebugWriter(os.Stderr))
|
|
}
|
|
|
|
client = hcloudimages.NewClient(hcloud.NewClient(opts...))
|
|
}
|
|
|
|
func Execute() {
|
|
err := rootCmd.Execute()
|
|
if err != nil {
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func init() {
|
|
rootCmd.SetErrPrefix("\033[1;31mError:")
|
|
|
|
rootCmd.PersistentFlags().CountVarP(&verbose, flagVerbose, "v", "verbose debug output, can be specified up to 2 times")
|
|
|
|
rootCmd.AddGroup(&cobra.Group{
|
|
ID: "primary",
|
|
Title: "Primary Commands:",
|
|
})
|
|
}
|