mirror of
https://github.com/apricote/hcloud-upload-image.git
synced 2025-08-06 22:06:58 +02:00
In #68 I reduced the general limits for the back off, thinking that it would speed up the upload on average because it was retrying faster. But because it was retrying faster, the 10 available retries were used up before SSH became available. The new 100 retries match the 3 minutes of total timeout that the previous solution had, and should fix all issues. In addition, I discovered that my implementation in `hcloudimages/backoff.ExponentialBackoffWithLimit` has a bug where the calculated offset could overflow before the limit was applied, resulting in negative durations. I did not fix the issue because `hcloud-go` provides such a method natively nowadays. Instead, I marked the method as deprecated, to be removed in a later release.
118 lines
2.9 KiB
Go
118 lines
2.9 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/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",
|
|
Short: `Manage custom OS images on Hetzner Cloud.`,
|
|
Long: `Manage custom OS images on Hetzner Cloud.`,
|
|
SilenceUsage: true,
|
|
DisableAutoGenTag: 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.WithPollOpts(hcloud.PollOpts{BackoffFunc: hcloud.ExponentialBackoffWithOpts(hcloud.ExponentialBackoffOpts{Multiplier: 2, Base: 1 * time.Second, Cap: 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:",
|
|
})
|
|
}
|