From be2611099a67e6f42bf81a4ec4cf9a06349c039f Mon Sep 17 00:00:00 2001 From: phm07 <22707808+phm07@users.noreply.github.com> Date: Thu, 18 Dec 2025 15:04:55 +0100 Subject: [PATCH 1/3] feat(server,primary-ip): deprecate datacenter --- .../manual/hcloud_primary-ip_create.md | 5 +- internal/cmd/primaryip/create.go | 25 ++++++++-- internal/cmd/primaryip/create_test.go | 8 +++- internal/cmd/primaryip/describe.go | 8 ++-- internal/cmd/primaryip/describe_test.go | 25 ++++------ internal/cmd/server/change_type.go | 2 +- internal/cmd/server/change_type_test.go | 4 +- internal/cmd/server/create.go | 3 ++ internal/cmd/server/describe.go | 8 ++-- internal/cmd/server/describe_test.go | 46 ++++++++----------- internal/cmd/server/list.go | 2 +- 11 files changed, 73 insertions(+), 63 deletions(-) diff --git a/docs/reference/manual/hcloud_primary-ip_create.md b/docs/reference/manual/hcloud_primary-ip_create.md index 61ee63f0..ebca5cda 100644 --- a/docs/reference/manual/hcloud_primary-ip_create.md +++ b/docs/reference/manual/hcloud_primary-ip_create.md @@ -9,12 +9,13 @@ hcloud primary-ip create [options] --type --name ### Options ``` - --assignee-id int Assignee (usually a Server) to assign Primary IP to (required if 'datacenter' is not specified) + --assignee-id int Assignee (usually a Server) to assign Primary IP to --auto-delete Delete Primary IP if assigned resource is deleted (true, false) - --datacenter string Datacenter (ID or name) (required if 'assignee-id' is not specified) + --datacenter string Datacenter (ID or name) (deprecated) --enable-protection strings Enable protection (delete) (default: none) -h, --help help for create --label stringToString User-defined labels ('key=value') (can be specified multiple times) (default []) + --location string Location of Primary IP --name string Name (required) -o, --output stringArray output options: json|yaml --type string Type (ipv4 or ipv6) (required) diff --git a/internal/cmd/primaryip/create.go b/internal/cmd/primaryip/create.go index 33a3ea50..27597fc6 100644 --- a/internal/cmd/primaryip/create.go +++ b/internal/cmd/primaryip/create.go @@ -28,9 +28,12 @@ var CreateCmd = base.CreateCmd[*hcloud.PrimaryIP]{ cmd.Flags().String("name", "", "Name (required)") _ = cmd.MarkFlagRequired("name") - cmd.Flags().Int64("assignee-id", 0, "Assignee (usually a Server) to assign Primary IP to (required if 'datacenter' is not specified)") + cmd.Flags().Int64("assignee-id", 0, "Assignee (usually a Server) to assign Primary IP to") - cmd.Flags().String("datacenter", "", "Datacenter (ID or name) (required if 'assignee-id' is not specified)") + cmd.Flags().String("location", "", "Location of Primary IP") + _ = cmd.RegisterFlagCompletionFunc("location", cmpl.SuggestCandidatesF(client.Location().Names)) + + cmd.Flags().String("datacenter", "", "Datacenter (ID or name) (deprecated)") _ = cmd.RegisterFlagCompletionFunc("datacenter", cmpl.SuggestCandidatesF(client.Datacenter().Names)) cmd.Flags().StringToString("label", nil, "User-defined labels ('key=value') (can be specified multiple times)") @@ -40,8 +43,8 @@ var CreateCmd = base.CreateCmd[*hcloud.PrimaryIP]{ cmd.Flags().Bool("auto-delete", false, "Delete Primary IP if assigned resource is deleted (true, false)") - cmd.MarkFlagsOneRequired("assignee-id", "datacenter") - cmd.MarkFlagsMutuallyExclusive("assignee-id", "datacenter") + cmd.MarkFlagsOneRequired("assignee-id", "datacenter", "location") + cmd.MarkFlagsMutuallyExclusive("assignee-id", "datacenter", "location") return cmd }, Run: func(s state.State, cmd *cobra.Command, _ []string) (*hcloud.PrimaryIP, any, error) { @@ -49,6 +52,7 @@ var CreateCmd = base.CreateCmd[*hcloud.PrimaryIP]{ name, _ := cmd.Flags().GetString("name") assigneeID, _ := cmd.Flags().GetInt64("assignee-id") datacenter, _ := cmd.Flags().GetString("datacenter") + locationIDOrName, _ := cmd.Flags().GetString("location") labels, _ := cmd.Flags().GetStringToString("label") protection, _ := cmd.Flags().GetStringSlice("enable-protection") autoDelete, _ := cmd.Flags().GetBool("auto-delete") @@ -71,6 +75,19 @@ var CreateCmd = base.CreateCmd[*hcloud.PrimaryIP]{ if cmd.Flags().Changed("auto-delete") { createOpts.AutoDelete = &autoDelete } + if cmd.Flags().Changed("location") { + location, _, err := s.Client().Location().Get(s, locationIDOrName) + if err != nil { + return nil, nil, err + } + if location == nil { + return nil, nil, fmt.Errorf("Location not found: %s", locationIDOrName) + } + createOpts.Location = location.Name + } + if cmd.Flags().Changed("datacenter") { + cmd.PrintErrln("Warning: The --datacenter flag is deprecated. Use --location instead.") + } result, _, err := s.Client().PrimaryIP().Create(s, createOpts) if err != nil { diff --git a/internal/cmd/primaryip/create_test.go b/internal/cmd/primaryip/create_test.go index 84937717..6b141a0f 100644 --- a/internal/cmd/primaryip/create_test.go +++ b/internal/cmd/primaryip/create_test.go @@ -62,12 +62,14 @@ func TestCreate(t *testing.T) { out, errOut, err := fx.Run(cmd, []string{"--name=my-ip", "--type=ipv4", "--datacenter=fsn1-dc14", "--auto-delete", "--label", "foo=bar"}) + expErr := "Warning: The --datacenter flag is deprecated. Use --location instead.\n" + expOut := `Primary IP 1 created IPv4: 192.168.2.1 ` require.NoError(t, err) - assert.Empty(t, errOut) + assert.Equal(t, expErr, errOut) assert.Equal(t, expOut, out) } @@ -124,7 +126,9 @@ func TestCreateJSON(t *testing.T) { jsonOut, out, err := fx.Run(cmd, []string{"-o=json", "--name=my-ip", "--type=ipv4", "--datacenter=fsn1-dc14", "--auto-delete", "--label", "foo=bar"}) - expOut := "Primary IP 1 created\n" + expOut := `Warning: The --datacenter flag is deprecated. Use --location instead. +Primary IP 1 created +` require.NoError(t, err) assert.Equal(t, expOut, out) diff --git a/internal/cmd/primaryip/describe.go b/internal/cmd/primaryip/describe.go index 94d30a69..288cc453 100644 --- a/internal/cmd/primaryip/describe.go +++ b/internal/cmd/primaryip/describe.go @@ -8,7 +8,7 @@ import ( "github.com/spf13/cobra" "github.com/hetznercloud/cli/internal/cmd/base" - "github.com/hetznercloud/cli/internal/cmd/datacenter" + "github.com/hetznercloud/cli/internal/cmd/location" "github.com/hetznercloud/cli/internal/cmd/util" "github.com/hetznercloud/cli/internal/hcapi2" "github.com/hetznercloud/cli/internal/state" @@ -26,7 +26,7 @@ var DescribeCmd = base.DescribeCmd[*hcloud.PrimaryIP]{ } return ip, hcloud.SchemaFromPrimaryIP(ip), nil }, - PrintText: func(s state.State, _ *cobra.Command, out io.Writer, primaryIP *hcloud.PrimaryIP) error { + PrintText: func(_ state.State, _ *cobra.Command, out io.Writer, primaryIP *hcloud.PrimaryIP) error { fmt.Fprintf(out, "ID:\t%d\n", primaryIP.ID) fmt.Fprintf(out, "Name:\t%s\n", primaryIP.Name) fmt.Fprintf(out, "Created:\t%s (%s)\n", util.Datetime(primaryIP.Created), humanize.Time(primaryIP.Created)) @@ -62,8 +62,8 @@ var DescribeCmd = base.DescribeCmd[*hcloud.PrimaryIP]{ util.DescribeLabels(out, primaryIP.Labels, "") fmt.Fprintln(out) - fmt.Fprintf(out, "Datacenter:\n") - fmt.Fprintf(out, "%s", util.PrefixLines(datacenter.DescribeDatacenter(s.Client(), primaryIP.Datacenter, true), " ")) + fmt.Fprintf(out, "Location:\n") + fmt.Fprintf(out, "%s", util.PrefixLines(location.DescribeLocation(primaryIP.Location), " ")) return nil }, } diff --git a/internal/cmd/primaryip/describe_test.go b/internal/cmd/primaryip/describe_test.go index 9d9a8c08..d2c982f6 100644 --- a/internal/cmd/primaryip/describe_test.go +++ b/internal/cmd/primaryip/describe_test.go @@ -35,7 +35,7 @@ func TestDescribe(t *testing.T) { Blocked: true, AutoDelete: false, AssigneeType: "server", - Datacenter: &hcloud.Datacenter{ID: 0, Location: &hcloud.Location{ID: 0}}, + Location: &hcloud.Location{ID: 0}, } fx.Client.PrimaryIPClient.EXPECT(). @@ -64,20 +64,15 @@ Protection: Labels: No labels -Datacenter: - ID: 0 - Name: - Description: - - Location: - ID: 0 - Name: - Description: - Network Zone: - Country: - City: - Latitude: 0.000000 - Longitude: 0.000000 +Location: + ID: 0 + Name: + Description: + Network Zone: + Country: + City: + Latitude: 0.000000 + Longitude: 0.000000 `, util.Datetime(primaryIP.Created), humanize.Time(primaryIP.Created)) require.NoError(t, err) diff --git a/internal/cmd/server/change_type.go b/internal/cmd/server/change_type.go index f1e5ec64..95afead4 100644 --- a/internal/cmd/server/change_type.go +++ b/internal/cmd/server/change_type.go @@ -47,7 +47,7 @@ var ChangeTypeCmd = base.Cmd{ return fmt.Errorf("Server Type not found: %s", serverTypeIDOrName) } - cmd.Print(deprecatedServerTypeWarning(serverType, server.Datacenter.Location.Name)) + cmd.Print(deprecatedServerTypeWarning(serverType, server.Location.Name)) keepDisk, _ := cmd.Flags().GetBool("keep-disk") opts := hcloud.ServerChangeTypeOpts{ diff --git a/internal/cmd/server/change_type_test.go b/internal/cmd/server/change_type_test.go index 64cd8133..f7c2b566 100644 --- a/internal/cmd/server/change_type_test.go +++ b/internal/cmd/server/change_type_test.go @@ -19,7 +19,7 @@ func TestChangeType(t *testing.T) { cmd := server.ChangeTypeCmd.CobraCommand(fx.State()) fx.ExpectEnsureToken() - srv := &hcloud.Server{ID: 123, Name: "my-server", Datacenter: &hcloud.Datacenter{Location: &hcloud.Location{Name: "fsn1"}}} + srv := &hcloud.Server{ID: 123, Name: "my-server", Location: &hcloud.Location{Name: "fsn1"}} st := &hcloud.ServerType{ID: 456, Name: "cax21", Locations: []hcloud.ServerTypeLocation{{Location: &hcloud.Location{Name: "fsn1"}}}} fx.Client.ServerClient.EXPECT(). @@ -53,7 +53,7 @@ func TestChangeTypeKeepDisk(t *testing.T) { cmd := server.ChangeTypeCmd.CobraCommand(fx.State()) fx.ExpectEnsureToken() - srv := &hcloud.Server{ID: 123, Name: "my-server", Datacenter: &hcloud.Datacenter{Location: &hcloud.Location{Name: "fsn1"}}} + srv := &hcloud.Server{ID: 123, Name: "my-server", Location: &hcloud.Location{Name: "fsn1"}} st := &hcloud.ServerType{ID: 456, Name: "cax21", Locations: []hcloud.ServerTypeLocation{{Location: &hcloud.Location{Name: "fsn1"}}}} fx.Client.ServerClient.EXPECT(). diff --git a/internal/cmd/server/create.go b/internal/cmd/server/create.go index 93e06e23..7194e942 100644 --- a/internal/cmd/server/create.go +++ b/internal/cmd/server/create.go @@ -433,6 +433,8 @@ func createOptsFromFlags( } if datacenterIDOrName != "" { + cmd.PrintErrln("Warning: The --datacenter flag is deprecated. Use --location instead.") + var datacenter *hcloud.Datacenter datacenter, _, err = s.Client().Datacenter().Get(s, datacenterIDOrName) if err != nil { @@ -444,6 +446,7 @@ func createOptsFromFlags( } createOpts.Datacenter = datacenter } + if locationIDOrName != "" { var location *hcloud.Location location, _, err = s.Client().Location().Get(s, locationIDOrName) diff --git a/internal/cmd/server/describe.go b/internal/cmd/server/describe.go index 01786634..501dc942 100644 --- a/internal/cmd/server/describe.go +++ b/internal/cmd/server/describe.go @@ -9,9 +9,9 @@ import ( "github.com/spf13/cobra" "github.com/hetznercloud/cli/internal/cmd/base" - "github.com/hetznercloud/cli/internal/cmd/datacenter" "github.com/hetznercloud/cli/internal/cmd/image" "github.com/hetznercloud/cli/internal/cmd/iso" + "github.com/hetznercloud/cli/internal/cmd/location" "github.com/hetznercloud/cli/internal/cmd/placementgroup" "github.com/hetznercloud/cli/internal/cmd/servertype" "github.com/hetznercloud/cli/internal/cmd/util" @@ -45,7 +45,7 @@ var DescribeCmd = base.DescribeCmd[*hcloud.Server]{ // As we already know the location the server is in, we can show the deprecation info // of that server type in that specific location. locationInfoIndex := slices.IndexFunc(server.ServerType.Locations, func(locInfo hcloud.ServerTypeLocation) bool { - return locInfo.Location.Name == server.Datacenter.Location.Name + return locInfo.Location.Name == server.Location.Name }) if locationInfoIndex >= 0 { if text := util.DescribeDeprecation(server.ServerType.Locations[locationInfoIndex]); text != "" { @@ -145,8 +145,8 @@ var DescribeCmd = base.DescribeCmd[*hcloud.Server]{ } fmt.Fprintln(out) - fmt.Fprintf(out, "Datacenter:\n") - fmt.Fprint(out, util.PrefixLines(datacenter.DescribeDatacenter(s.Client(), server.Datacenter, true), " ")) + fmt.Fprintf(out, "Location:\n") + fmt.Fprint(out, util.PrefixLines(location.DescribeLocation(server.Location), " ")) fmt.Fprintln(out) if server.BackupWindow != "" { diff --git a/internal/cmd/server/describe_test.go b/internal/cmd/server/describe_test.go index ae4f4e6b..005ffa32 100644 --- a/internal/cmd/server/describe_test.go +++ b/internal/cmd/server/describe_test.go @@ -66,20 +66,15 @@ func TestDescribe(t *testing.T) { "key": "value", }, }, - Datacenter: &hcloud.Datacenter{ - ID: 4, - Name: "hel1-dc2", - Location: &hcloud.Location{ - ID: 3, - Name: "hel1", - Description: "Helsinki DC Park 1", - NetworkZone: "eu-central", - Country: "FI", - City: "Helsinki", - Latitude: 60.169855, - Longitude: 24.938379, - }, - Description: "Helsinki 1 virtual DC 2", + Location: &hcloud.Location{ + ID: 3, + Name: "hel1", + Description: "Helsinki DC Park 1", + NetworkZone: "eu-central", + Country: "FI", + City: "Helsinki", + Latitude: 60.169855, + Longitude: 24.938379, }, IncludedTraffic: 20 * util.Tebibyte, Protection: hcloud.ServerProtection{Delete: true, Rebuild: true}, @@ -149,20 +144,15 @@ Image: Labels: key: value -Datacenter: - ID: 4 - Name: hel1-dc2 - Description: Helsinki 1 virtual DC 2 - - Location: - ID: 3 - Name: hel1 - Description: Helsinki DC Park 1 - Network Zone: eu-central - Country: FI - City: Helsinki - Latitude: 60.169855 - Longitude: 24.938379 +Location: + ID: 3 + Name: hel1 + Description: Helsinki DC Park 1 + Network Zone: eu-central + Country: FI + City: Helsinki + Latitude: 60.169855 + Longitude: 24.938379 Backup Window: Backups disabled diff --git a/internal/cmd/server/list.go b/internal/cmd/server/list.go index 4f69adae..18cc7397 100644 --- a/internal/cmd/server/list.go +++ b/internal/cmd/server/list.go @@ -92,7 +92,7 @@ var ListCmd = &base.ListCmd[*hcloud.Server, schema.Server]{ return server.Datacenter.Name }). AddFieldFn("location", func(server *hcloud.Server) string { - return server.Datacenter.Location.Name + return server.Location.Name }). AddFieldFn("labels", func(server *hcloud.Server) string { return util.LabelsToString(server.Labels) From 134b7256390afd920c52bc355a9a9f9b9fc976bb Mon Sep 17 00:00:00 2001 From: phm07 <22707808+phm07@users.noreply.github.com> Date: Fri, 2 Jan 2026 14:22:04 +0100 Subject: [PATCH 2/3] more docs --- docs/reference/manual/hcloud_primary-ip_create.md | 7 +++++++ docs/reference/manual/hcloud_server_create.md | 9 ++++++++- internal/cmd/primaryip/create.go | 10 +++++++--- internal/cmd/server/create.go | 6 +++++- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/docs/reference/manual/hcloud_primary-ip_create.md b/docs/reference/manual/hcloud_primary-ip_create.md index ebca5cda..9bd7134f 100644 --- a/docs/reference/manual/hcloud_primary-ip_create.md +++ b/docs/reference/manual/hcloud_primary-ip_create.md @@ -2,6 +2,13 @@ Create a Primary IP +### Synopsis + +Create a Primary IP. + +The --datacenter flag is deprecated. Use --location or --assignee-id instead. +See https://docs.hetzner.cloud/changelog#2025-12-16-phasing-out-datacenters + ``` hcloud primary-ip create [options] --type --name ``` diff --git a/docs/reference/manual/hcloud_server_create.md b/docs/reference/manual/hcloud_server_create.md index 6ab4f586..8ab4864e 100644 --- a/docs/reference/manual/hcloud_server_create.md +++ b/docs/reference/manual/hcloud_server_create.md @@ -2,6 +2,13 @@ Create a Server +### Synopsis + +Create a Server. + +The --datacenter flag is deprecated. Use --location instead. +See https://docs.hetzner.cloud/changelog#2025-12-16-phasing-out-datacenters + ``` hcloud server create [options] --name --type --image ``` @@ -11,7 +18,7 @@ hcloud server create [options] --name --type --image --name ", - Short: "Create a Primary IP", + Use: "create [options] --type --name ", + Short: "Create a Primary IP", + Long: `Create a Primary IP. + +The --datacenter flag is deprecated. Use --location or --assignee-id instead. +See https://docs.hetzner.cloud/changelog#2025-12-16-phasing-out-datacenters`, TraverseChildren: true, DisableFlagsInUseLine: true, } @@ -86,7 +90,7 @@ var CreateCmd = base.CreateCmd[*hcloud.PrimaryIP]{ createOpts.Location = location.Name } if cmd.Flags().Changed("datacenter") { - cmd.PrintErrln("Warning: The --datacenter flag is deprecated. Use --location instead.") + cmd.PrintErrln("Warning: The --datacenter flag is deprecated. Use --location or --assignee-id instead.") } result, _, err := s.Client().PrimaryIP().Create(s, createOpts) diff --git a/internal/cmd/server/create.go b/internal/cmd/server/create.go index 7194e942..4c48f94a 100644 --- a/internal/cmd/server/create.go +++ b/internal/cmd/server/create.go @@ -41,6 +41,10 @@ var CreateCmd = base.CreateCmd[*createResult]{ cmd := &cobra.Command{ Use: "create [options] --name --type --image ", Short: "Create a Server", + Long: `Create a Server. + +The --datacenter flag is deprecated. Use --location instead. +See https://docs.hetzner.cloud/changelog#2025-12-16-phasing-out-datacenters`, } cmd.Flags().String("name", "", "Server name (required)") @@ -57,7 +61,7 @@ var CreateCmd = base.CreateCmd[*createResult]{ cmd.Flags().String("location", "", "Location (ID or name)") _ = cmd.RegisterFlagCompletionFunc("location", cmpl.SuggestCandidatesF(client.Location().Names)) - cmd.Flags().String("datacenter", "", "Datacenter (ID or name)") + cmd.Flags().String("datacenter", "", "Datacenter (ID or name) (deprecated)") _ = cmd.RegisterFlagCompletionFunc("datacenter", cmpl.SuggestCandidatesF(client.Datacenter().Names)) cmd.Flags().StringSlice("ssh-key", nil, "ID or name of SSH Key to inject (can be specified multiple times)") From 921e4ae3f78b7fb30b6e38422fcd55c22f307b77 Mon Sep 17 00:00:00 2001 From: phm07 <22707808+phm07@users.noreply.github.com> Date: Fri, 2 Jan 2026 14:48:47 +0100 Subject: [PATCH 3/3] fix test --- internal/cmd/primaryip/create_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/cmd/primaryip/create_test.go b/internal/cmd/primaryip/create_test.go index 6b141a0f..c90e0fe3 100644 --- a/internal/cmd/primaryip/create_test.go +++ b/internal/cmd/primaryip/create_test.go @@ -62,7 +62,7 @@ func TestCreate(t *testing.T) { out, errOut, err := fx.Run(cmd, []string{"--name=my-ip", "--type=ipv4", "--datacenter=fsn1-dc14", "--auto-delete", "--label", "foo=bar"}) - expErr := "Warning: The --datacenter flag is deprecated. Use --location instead.\n" + expErr := "Warning: The --datacenter flag is deprecated. Use --location or --assignee-id instead.\n" expOut := `Primary IP 1 created IPv4: 192.168.2.1 @@ -126,7 +126,7 @@ func TestCreateJSON(t *testing.T) { jsonOut, out, err := fx.Run(cmd, []string{"-o=json", "--name=my-ip", "--type=ipv4", "--datacenter=fsn1-dc14", "--auto-delete", "--label", "foo=bar"}) - expOut := `Warning: The --datacenter flag is deprecated. Use --location instead. + expOut := `Warning: The --datacenter flag is deprecated. Use --location or --assignee-id instead. Primary IP 1 created `