Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions internal/cmd/server/change_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,7 @@ var ChangeTypeCmd = base.Cmd{
return fmt.Errorf("Server Type not found: %s", serverTypeIDOrName)
}

if serverType.IsDeprecated() {
cmd.Print(warningDeprecatedServerType(serverType))
}
cmd.Print(deprecatedServerTypeWarning(serverType, server.Datacenter.Location.Name))

keepDisk, _ := cmd.Flags().GetBool("keep-disk")
opts := hcloud.ServerChangeTypeOpts{
Expand Down
8 changes: 4 additions & 4 deletions internal/cmd/server/change_type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ func TestChangeType(t *testing.T) {
cmd := server.ChangeTypeCmd.CobraCommand(fx.State())
fx.ExpectEnsureToken()

srv := &hcloud.Server{ID: 123, Name: "my-server"}
st := &hcloud.ServerType{ID: 456, Name: "cax21"}
srv := &hcloud.Server{ID: 123, Name: "my-server", Datacenter: &hcloud.Datacenter{Location: &hcloud.Location{Name: "fsn1"}}}
st := &hcloud.ServerType{ID: 456, Name: "cax21", Locations: []hcloud.ServerTypeLocation{{Location: &hcloud.Location{Name: "fsn1"}}}}

fx.Client.ServerClient.EXPECT().
Get(gomock.Any(), "my-server").
Expand Down Expand Up @@ -53,8 +53,8 @@ func TestChangeTypeKeepDisk(t *testing.T) {
cmd := server.ChangeTypeCmd.CobraCommand(fx.State())
fx.ExpectEnsureToken()

srv := &hcloud.Server{ID: 123, Name: "my-server"}
st := &hcloud.ServerType{ID: 456, Name: "cax21"}
srv := &hcloud.Server{ID: 123, Name: "my-server", Datacenter: &hcloud.Datacenter{Location: &hcloud.Location{Name: "fsn1"}}}
st := &hcloud.ServerType{ID: 456, Name: "cax21", Locations: []hcloud.ServerTypeLocation{{Location: &hcloud.Location{Name: "fsn1"}}}}

fx.Client.ServerClient.EXPECT().
Get(gomock.Any(), "my-server").
Expand Down
45 changes: 35 additions & 10 deletions internal/cmd/server/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,16 @@ var CreateCmd = base.CreateCmd[*createResult]{
return nil, nil, err
}

// Check if intended server type is deprecated in the requested location
var locName string
if createOpts.Location != nil {
locName = createOpts.Location.Name
} else if createOpts.Datacenter != nil {
locName = createOpts.Datacenter.Location.Name
}

cmd.Print(deprecatedServerTypeWarning(createOpts.ServerType, locName))

result, _, err := s.Client().Server().Create(s, createOpts)
if err != nil {
return nil, nil, err
Expand Down Expand Up @@ -244,8 +254,8 @@ func createOptsFromFlags(
name, _ := flags.GetString("name")
serverTypeName, _ := flags.GetString("type")
imageIDorName, _ := flags.GetString("image")
location, _ := flags.GetString("location")
datacenter, _ := flags.GetString("datacenter")
locationIDOrName, _ := flags.GetString("location")
datacenterIDOrName, _ := flags.GetString("datacenter")
userDataFiles, _ := flags.GetStringArray("user-data-from-file")
startAfterCreate, _ := flags.GetBool("start-after-create")
sshKeys, _ := flags.GetStringSlice("ssh-key")
Expand All @@ -271,10 +281,6 @@ func createOptsFromFlags(
return
}

if serverType.IsDeprecated() {
cmd.Print(warningDeprecatedServerType(serverType))
}

// Select correct image based on Server Type architecture
image, _, err := s.Client().Image().GetForArchitecture(s, imageIDorName, serverType.Architecture)
if err != nil {
Expand Down Expand Up @@ -424,12 +430,31 @@ func createOptsFromFlags(
createOpts.Firewalls = append(createOpts.Firewalls, &hcloud.ServerCreateFirewall{Firewall: *firewall})
}

if datacenter != "" {
createOpts.Datacenter = &hcloud.Datacenter{Name: datacenter}
if datacenterIDOrName != "" {
var datacenter *hcloud.Datacenter
datacenter, _, err = s.Client().Datacenter().Get(s, datacenterIDOrName)
if err != nil {
return
}
if datacenter == nil {
err = fmt.Errorf("Datacenter not found: %s", datacenterIDOrName)
return
}
createOpts.Datacenter = datacenter
}
if location != "" {
createOpts.Location = &hcloud.Location{Name: location}
if locationIDOrName != "" {
var location *hcloud.Location
location, _, err = s.Client().Location().Get(s, locationIDOrName)
if err != nil {
return
}
if location == nil {
err = fmt.Errorf("Location not found: %s", locationIDOrName)
return
}
createOpts.Location = location
}

if placementGroupIDorName != "" {
var placementGroup *hcloud.PlacementGroup
placementGroup, _, err = s.Client().PlacementGroup().Get(s, placementGroupIDorName)
Expand Down
6 changes: 3 additions & 3 deletions internal/cmd/server/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func TestCreate(t *testing.T) {

fx.Client.ServerTypeClient.EXPECT().
Get(gomock.Any(), "cx22").
Return(&hcloud.ServerType{Architecture: hcloud.ArchitectureX86}, nil, nil)
Return(&hcloud.ServerType{Architecture: hcloud.ArchitectureX86, Locations: []hcloud.ServerTypeLocation{{Location: &hcloud.Location{Name: "fsn1"}}}}, nil, nil)
fx.Client.ImageClient.EXPECT().
GetForArchitecture(gomock.Any(), "ubuntu-20.04", hcloud.ArchitectureX86).
Return(&hcloud.Image{}, nil, nil)
Expand Down Expand Up @@ -193,7 +193,7 @@ func TestCreateJSON(t *testing.T) {

fx.Client.ServerTypeClient.EXPECT().
Get(gomock.Any(), "cx22").
Return(&hcloud.ServerType{Architecture: hcloud.ArchitectureX86}, nil, nil)
Return(&hcloud.ServerType{Architecture: hcloud.ArchitectureX86, Locations: []hcloud.ServerTypeLocation{{Location: &hcloud.Location{Name: "fsn1"}}}}, nil, nil)
fx.Client.ImageClient.EXPECT().
GetForArchitecture(gomock.Any(), "ubuntu-20.04", hcloud.ArchitectureX86).
Return(&hcloud.Image{}, nil, nil)
Expand Down Expand Up @@ -236,7 +236,7 @@ func TestCreateProtectionBackup(t *testing.T) {

fx.Client.ServerTypeClient.EXPECT().
Get(gomock.Any(), "cx22").
Return(&hcloud.ServerType{Architecture: hcloud.ArchitectureX86}, nil, nil)
Return(&hcloud.ServerType{Architecture: hcloud.ArchitectureX86, Locations: []hcloud.ServerTypeLocation{{Location: &hcloud.Location{Name: "fsn1"}}}}, nil, nil)
fx.Client.ImageClient.EXPECT().
GetForArchitecture(gomock.Any(), "ubuntu-20.04", hcloud.ArchitectureX86).
Return(&hcloud.Image{}, nil, nil)
Expand Down
12 changes: 10 additions & 2 deletions internal/cmd/server/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package server

import (
"fmt"
"slices"

humanize "github.com/dustin/go-humanize"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -40,8 +41,15 @@ var DescribeCmd = base.DescribeCmd[*hcloud.Server]{
cmd.Printf(" Disk:\t\t%d GB\n", server.PrimaryDiskSize)
cmd.Printf(" Storage Type:\t%s\n", server.ServerType.StorageType)

if text := util.DescribeDeprecation(server.ServerType); text != "" {
cmd.Print(util.PrefixLines(text, " "))
// 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
})
if locationInfoIndex >= 0 {
if text := util.DescribeDeprecation(server.ServerType.Locations[locationInfoIndex]); text != "" {
cmd.Print(util.PrefixLines(text, " "))
}
}

cmd.Printf("Public Net:\n")
Expand Down
22 changes: 21 additions & 1 deletion internal/cmd/server/describe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ func TestDescribe(t *testing.T) {
cmd := server.DescribeCmd.CobraCommand(fx.State())
fx.ExpectEnsureToken()

serverTypeDeprecation := hcloud.DeprecatableResource{Deprecation: &hcloud.DeprecationInfo{
Announced: time.Date(2036, 1, 1, 0, 0, 0, 0, time.UTC),
UnavailableAfter: time.Date(2036, 4, 1, 0, 0, 0, 0, time.UTC),
}}

srv := &hcloud.Server{
ID: 123,
Name: "test",
Expand All @@ -37,6 +42,15 @@ func TestDescribe(t *testing.T) {
Memory: 4.0,
Disk: 40,
StorageType: hcloud.StorageTypeLocal,
Locations: []hcloud.ServerTypeLocation{
{
Location: &hcloud.Location{Name: "fsn1"},
},
{
Location: &hcloud.Location{Name: "hel1"},
DeprecatableResource: serverTypeDeprecation,
},
},
},
Image: &hcloud.Image{
ID: 123,
Expand Down Expand Up @@ -91,6 +105,9 @@ Server Type: cax11 (ID: 45)
Memory: 4 GB
Disk: 0 GB
Storage Type: local
Deprecation:
Announced: %s (%s)
Unavailable After: %s (%s)
Public Net:
IPv4:
No Primary IPv4
Expand Down Expand Up @@ -142,7 +159,10 @@ Placement Group:
No Placement Group set
`,
util.Datetime(srv.Created), humanize.Time(srv.Created),
util.Datetime(srv.Image.Created), humanize.Time(srv.Image.Created))
util.Datetime(serverTypeDeprecation.DeprecationAnnounced()), humanize.Time(serverTypeDeprecation.DeprecationAnnounced()),
util.Datetime(serverTypeDeprecation.UnavailableAfter()), humanize.Time(serverTypeDeprecation.UnavailableAfter()),
util.Datetime(srv.Image.Created), humanize.Time(srv.Image.Created),
)

require.NoError(t, err)
assert.Empty(t, errOut)
Expand Down
18 changes: 10 additions & 8 deletions internal/cmd/server/texts.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@ package server

import (
"fmt"
"time"

"github.com/hetznercloud/hcloud-go/v2/hcloud"
"github.com/hetznercloud/hcloud-go/v2/hcloud/exp/deprecationutil"
)

func warningDeprecatedServerType(serverType *hcloud.ServerType) string {
if !serverType.IsDeprecated() {
return ""
}
const ChangeDeprecatedServerTypeMessage = (`Existing servers of that plan will ` +
`continue to work as before and no action is required on your part. ` +
`It is possible to migrate this Server to another Server Type by using ` +
`the "hcloud server change-type" command.`)

if time.Now().After(serverType.UnavailableAfter()) {
return fmt.Sprintf("Attention: The Server Type %q is deprecated and can no longer be ordered. Existing servers of that plan will continue to work as before and no action is required on your part. It is possible to migrate this Server to another Server Type by using the \"hcloud server change-type\" command.\n\n", serverType.Name)
func deprecatedServerTypeWarning(serverType *hcloud.ServerType, locationName string) string {
warnText, _ := deprecationutil.ServerTypeWarning(serverType, locationName)
if warnText == "" {
return ""
}

return fmt.Sprintf("Attention: The Server Type %q is deprecated and will no longer be available for order as of %s. Existing servers of that plan will continue to work as before and no action is required on your part. It is possible to migrate this Server to another Server Type by using the \"hcloud server change-type\" command.\n\n", serverType.Name, serverType.UnavailableAfter().Format(time.DateOnly))
return fmt.Sprintf("Attention: %s %s\n\n", warnText, ChangeDeprecatedServerTypeMessage)
}
51 changes: 41 additions & 10 deletions internal/cmd/servertype/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,28 @@ var DescribeCmd = base.DescribeCmd[*hcloud.ServerType]{
cmd.Printf("Memory:\t\t\t%.1f GB\n", serverType.Memory)
cmd.Printf("Disk:\t\t\t%d GB\n", serverType.Disk)
cmd.Printf("Storage Type:\t\t%s\n", serverType.StorageType)
cmd.Print(util.DescribeDeprecation(serverType))

pricings, err := fullPricingInfo(s, serverType)
if err != nil {
cmd.PrintErrf("failed to get prices for Server Type: %v", err)
}

if pricings != nil {
cmd.Printf("Pricings per Location:\n")
for _, price := range pricings {
cmd.Printf(" - Location:\t\t%s\n", price.Location.Name)
cmd.Printf(" Hourly:\t\t%s\n", util.GrossPrice(price.Hourly))
cmd.Printf(" Monthly:\t\t%s\n", util.GrossPrice(price.Monthly))
cmd.Printf(" Included Traffic:\t%s\n", humanize.IBytes(price.IncludedTraffic))
cmd.Printf(" Additional Traffic:\t%s per TB\n", util.GrossPrice(price.PerTBTraffic))
cmd.Printf("\n")
locations := joinLocationInfo(serverType, pricings)
cmd.Printf("Locations:\n")
for _, info := range locations {

cmd.Printf(" - Location:\t\t%s\n", info.Location.Name)

if deprecationText := util.DescribeDeprecation(info); deprecationText != "" {
cmd.Print(util.PrefixLines(deprecationText, " "))
}

cmd.Printf(" Pricing:\n")
cmd.Printf(" Hourly:\t\t%s\n", util.GrossPrice(info.Pricing.Hourly))
cmd.Printf(" Monthly:\t\t%s\n", util.GrossPrice(info.Pricing.Monthly))
cmd.Printf(" Included Traffic:\t%s\n", humanize.IBytes(info.Pricing.IncludedTraffic))
cmd.Printf(" Additional Traffic:\t%s per TB\n", util.GrossPrice(info.Pricing.PerTBTraffic))
cmd.Printf("\n")
}

return nil
Expand All @@ -70,3 +75,29 @@ func fullPricingInfo(s state.State, serverType *hcloud.ServerType) ([]hcloud.Ser

return nil, nil
}

type locationInfo struct {
Location *hcloud.Location
hcloud.DeprecatableResource
Pricing hcloud.ServerTypeLocationPricing
}

func joinLocationInfo(serverType *hcloud.ServerType, pricings []hcloud.ServerTypeLocationPricing) []locationInfo {
locations := make([]locationInfo, 0, len(serverType.Locations))

for _, location := range serverType.Locations {
info := locationInfo{Location: location.Location, DeprecatableResource: location.DeprecatableResource}

for _, pricing := range pricings {
// Pricing endpoint only sets the location name
if pricing.Location.Name == info.Location.Name {
info.Pricing = pricing
break
}
}

locations = append(locations, info)
}

return locations
}
Loading
Loading