From f8e32b2939f8da9b434372dc567a3127c55ddba4 Mon Sep 17 00:00:00 2001 From: phm07 <22707808+phm07@users.noreply.github.com> Date: Thu, 22 Jan 2026 14:41:08 +0100 Subject: [PATCH 1/3] feat(server): allow specifying user-data for rebuild --- .../reference/manual/hcloud_server_rebuild.md | 9 +++--- internal/cmd/server/rebuild.go | 29 ++++++++++++++++++- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/docs/reference/manual/hcloud_server_rebuild.md b/docs/reference/manual/hcloud_server_rebuild.md index e8fb6160..876a26a2 100644 --- a/docs/reference/manual/hcloud_server_rebuild.md +++ b/docs/reference/manual/hcloud_server_rebuild.md @@ -3,15 +3,16 @@ Rebuild a server ``` -hcloud server rebuild [--allow-deprecated-image] --image +hcloud server rebuild [options] --image ``` ### Options ``` - --allow-deprecated-image Enable the use of deprecated images (default: false) (true, false) - -h, --help help for rebuild - --image string ID or name of Image to rebuild from (required) + --allow-deprecated-image Enable the use of deprecated images (default: false) (true, false) + -h, --help help for rebuild + --image string ID or name of Image to rebuild from (required) + --user-data-from-file stringArray Read user data from specified file (use - to read from stdin) ``` ### Options inherited from parent commands diff --git a/internal/cmd/server/rebuild.go b/internal/cmd/server/rebuild.go index f9cf3c42..1242a997 100644 --- a/internal/cmd/server/rebuild.go +++ b/internal/cmd/server/rebuild.go @@ -2,6 +2,8 @@ package server import ( "fmt" + "io" + "os" "time" "github.com/spf13/cobra" @@ -16,7 +18,7 @@ import ( var RebuildCmd = base.Cmd{ BaseCobraCommand: func(client hcapi2.Client) *cobra.Command { cmd := &cobra.Command{ - Use: "rebuild [--allow-deprecated-image] --image ", + Use: "rebuild [options] --image ", Short: "Rebuild a server", ValidArgsFunction: cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)), TraverseChildren: true, @@ -26,8 +28,11 @@ var RebuildCmd = base.Cmd{ cmd.Flags().String("image", "", "ID or name of Image to rebuild from (required)") _ = cmd.RegisterFlagCompletionFunc("image", cmpl.SuggestCandidatesF(client.Image().Names)) _ = cmd.MarkFlagRequired("image") + cmd.Flags().Bool("allow-deprecated-image", false, "Enable the use of deprecated images (default: false) (true, false)") + cmd.Flags().StringArray("user-data-from-file", []string{}, "Read user data from specified file (use - to read from stdin)") + return cmd }, Run: func(s state.State, cmd *cobra.Command, args []string) error { @@ -41,6 +46,8 @@ var RebuildCmd = base.Cmd{ } imageIDOrName, _ := cmd.Flags().GetString("image") + userDataFiles, _ := cmd.Flags().GetStringArray("user-data-from-file") + // Select correct image based on Server Type architecture image, _, err := s.Client().Image().GetForArchitecture(s, imageIDOrName, server.ServerType.Architecture) if err != nil { @@ -63,6 +70,26 @@ var RebuildCmd = base.Cmd{ opts := hcloud.ServerRebuildOpts{ Image: image, } + if len(userDataFiles) == 1 { + var data []byte + if userDataFiles[0] == "-" { + data, err = io.ReadAll(os.Stdin) + } else { + data, err = os.ReadFile(userDataFiles[0]) + } + if err != nil { + return err + } + userData := string(data) + opts.UserData = &userData + } else if len(userDataFiles) > 1 { + userData, err := buildUserData(userDataFiles) + if err != nil { + return err + } + opts.UserData = &userData + } + result, _, err := s.Client().Server().RebuildWithResult(s, server, opts) if err != nil { return err From 9adf99d75eb4c8b57558a406af030bb3c1b64095 Mon Sep 17 00:00:00 2001 From: phm07 <22707808+phm07@users.noreply.github.com> Date: Fri, 23 Jan 2026 11:56:58 +0100 Subject: [PATCH 2/3] refactor --- internal/cmd/server/create.go | 41 +++++++++++++++++----------------- internal/cmd/server/rebuild.go | 17 ++------------ 2 files changed, 23 insertions(+), 35 deletions(-) diff --git a/internal/cmd/server/create.go b/internal/cmd/server/create.go index ab5da232..4a1bd997 100644 --- a/internal/cmd/server/create.go +++ b/internal/cmd/server/create.go @@ -206,6 +206,23 @@ func detectContentType(data string) string { } func buildUserData(files []string) (string, error) { + if len(files) <= 0 { + return "", nil + } + + var ( + data []byte + err error + ) + if len(files) == 1 { + if file := files[0]; file == "-" { + data, err = io.ReadAll(os.Stdin) + } else { + data, err = os.ReadFile(file) + } + return string(data), err + } + var ( buf = new(bytes.Buffer) mp = multipart.NewWriter(buf) @@ -215,10 +232,6 @@ func buildUserData(files []string) (string, error) { fmt.Fprint(buf, "Content-Type: multipart/mixed; boundary="+mp.Boundary()+"\r\n\r\n") for _, file := range files { - var ( - data []byte - err error - ) if file == "-" { data, err = io.ReadAll(os.Stdin) } else { @@ -352,22 +365,10 @@ func createOptsFromFlags( publicNetConfiguration.IPv6 = primaryIPv6 } createOpts.PublicNet = publicNetConfiguration - if len(userDataFiles) == 1 { - var data []byte - if userDataFiles[0] == "-" { - data, err = io.ReadAll(os.Stdin) - } else { - data, err = os.ReadFile(userDataFiles[0]) - } - if err != nil { - return - } - createOpts.UserData = string(data) - } else if len(userDataFiles) > 1 { - createOpts.UserData, err = buildUserData(userDataFiles) - if err != nil { - return - } + + createOpts.UserData, err = buildUserData(userDataFiles) + if err != nil { + return } if !flags.Changed("ssh-key") && config.OptionDefaultSSHKeys.Changed(s.Config()) { diff --git a/internal/cmd/server/rebuild.go b/internal/cmd/server/rebuild.go index 1242a997..e1f5372c 100644 --- a/internal/cmd/server/rebuild.go +++ b/internal/cmd/server/rebuild.go @@ -2,8 +2,6 @@ package server import ( "fmt" - "io" - "os" "time" "github.com/spf13/cobra" @@ -70,19 +68,8 @@ var RebuildCmd = base.Cmd{ opts := hcloud.ServerRebuildOpts{ Image: image, } - if len(userDataFiles) == 1 { - var data []byte - if userDataFiles[0] == "-" { - data, err = io.ReadAll(os.Stdin) - } else { - data, err = os.ReadFile(userDataFiles[0]) - } - if err != nil { - return err - } - userData := string(data) - opts.UserData = &userData - } else if len(userDataFiles) > 1 { + + if len(userDataFiles) > 0 { userData, err := buildUserData(userDataFiles) if err != nil { return err From 7b30e0ef0bff7c6e0b6d3d3a84d74c3ec9e50df1 Mon Sep 17 00:00:00 2001 From: phm07 <22707808+phm07@users.noreply.github.com> Date: Fri, 23 Jan 2026 12:44:39 +0100 Subject: [PATCH 3/3] fix lint --- internal/cmd/server/create.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/server/create.go b/internal/cmd/server/create.go index 4a1bd997..c8b16cd5 100644 --- a/internal/cmd/server/create.go +++ b/internal/cmd/server/create.go @@ -206,7 +206,7 @@ func detectContentType(data string) string { } func buildUserData(files []string) (string, error) { - if len(files) <= 0 { + if len(files) == 0 { return "", nil }