Skip to content

Commit 6639f1a

Browse files
authored
Merge pull request #20 from serverscom/improve-ds
Improve hosts ds cmd
2 parents df8df43 + 307b30b commit 6639f1a

7 files changed

Lines changed: 554 additions & 84 deletions

File tree

cmd/base/flags.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ func AddGlobalFlags(cmd *cobra.Command) {
1111
cmd.PersistentFlags().StringP("output", "o", "text", "output format (text/json/yaml)")
1212
// define help flag without shorthand before cobra adds it by default to avoid conflict with no-header flag shorthand
1313
cmd.PersistentFlags().Bool("help", false, "Print usage")
14-
cmd.PersistentFlags().Lookup("help").Hidden = true
1514
cmd.PersistentFlags().BoolP("no-header", "h", false, "print output without headers")
1615
}
1716

cmd/entities/hosts/add.go

Lines changed: 184 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,210 @@
11
package hosts
22

33
import (
4-
"context"
54
"fmt"
65
"log"
6+
"os"
7+
8+
"maps"
79

810
serverscom "github.com/serverscom/serverscom-go-client/pkg"
911
"github.com/serverscom/srvctl/cmd/base"
1012
"github.com/spf13/cobra"
13+
"github.com/spf13/pflag"
1114
)
1215

13-
type HostCreator interface {
14-
Create(ctx context.Context, client *serverscom.Client, input any) (any, error)
15-
NewCreateInput() any
16+
type AddDSFlags struct {
17+
InputPath string
18+
LocationID int
19+
ServerModelID int
20+
OperatingSystemID int
21+
Features []string
22+
RAMSize int
23+
PublicUplinkID int
24+
PublicBandwidthID int
25+
PrivateUplinkID int
26+
DriveSlots map[string]int
27+
Layout []string
28+
Partitions []string
29+
IPv6 bool
30+
UserDataFile string
31+
UserData string
32+
Labels map[string]string
1633
}
1734

18-
type DSCreateMgr struct{}
35+
func applyFlagsToInput(
36+
input *serverscom.DedicatedServerCreateInput,
37+
flags *AddDSFlags,
38+
pflags *pflag.FlagSet,
39+
) error {
40+
if pflags.Changed("location-id") {
41+
input.LocationID = int64(flags.LocationID)
42+
}
43+
if pflags.Changed("server-model-id") {
44+
input.ServerModelID = int64(flags.ServerModelID)
45+
}
46+
if pflags.Changed("operating-system-id") {
47+
val := int64(flags.OperatingSystemID)
48+
input.OperatingSystemID = &val
49+
}
50+
if pflags.Changed("feature") {
51+
input.Features = flags.Features
52+
}
53+
if pflags.Changed("ram-size") {
54+
input.RAMSize = flags.RAMSize
55+
}
56+
if pflags.Changed("public-uplink-id") {
57+
if input.UplinkModels.Public == nil {
58+
input.UplinkModels.Public = &serverscom.DedicatedServerPublicUplinkInput{}
59+
}
60+
input.UplinkModels.Public.ID = int64(flags.PublicUplinkID)
61+
}
62+
if pflags.Changed("public-bandwidth-id") {
63+
if input.UplinkModels.Public == nil {
64+
input.UplinkModels.Public = &serverscom.DedicatedServerPublicUplinkInput{}
65+
}
66+
input.UplinkModels.Public.BandwidthModelID = int64(flags.PublicBandwidthID)
67+
}
68+
if pflags.Changed("private-uplink-id") {
69+
input.UplinkModels.Private.ID = int64(flags.PrivateUplinkID)
70+
}
1971

20-
func (c *DSCreateMgr) Create(ctx context.Context, client *serverscom.Client, input any) (any, error) {
21-
dsInput, ok := input.(*serverscom.DedicatedServerCreateInput)
22-
if !ok {
23-
return nil, fmt.Errorf("invalid input type for dedicated server")
72+
if pflags.Changed("drive-slots") {
73+
slots, err := parseDriveSlots(flags.DriveSlots)
74+
if err != nil {
75+
return err
76+
}
77+
input.Drives.Slots = slots
2478
}
25-
return client.Hosts.CreateDedicatedServers(ctx, *dsInput)
26-
}
2779

28-
func (c *DSCreateMgr) NewCreateInput() any {
29-
return &serverscom.DedicatedServerCreateInput{}
30-
}
80+
if pflags.Changed("layout") {
81+
layouts, err := parseLayout(flags.Layout)
82+
if err != nil {
83+
return err
84+
}
85+
input.Drives.Layout = mergeLayouts(input.Drives.Layout, layouts)
86+
}
3187

32-
type SBMCreateMgr struct{}
88+
if pflags.Changed("partition") {
89+
if len(input.Drives.Layout) == 0 {
90+
return fmt.Errorf("partition given but layout is empty")
91+
}
92+
partitions, err := parsePartitions(flags.Partitions)
93+
if err != nil {
94+
return err
95+
}
96+
err = applyPartitions(input.Drives.Layout, partitions)
97+
if err != nil {
98+
return err
99+
}
100+
}
101+
if pflags.Changed("ipv6") {
102+
input.IPv6 = flags.IPv6
103+
}
104+
if pflags.Changed("user-data") && pflags.Changed("user-data-file") {
105+
return fmt.Errorf("'user-data' and 'user-data-file' can't be used together")
106+
}
107+
if pflags.Changed("user-data-file") {
108+
data, err := os.ReadFile(flags.UserDataFile)
109+
if err != nil {
110+
return fmt.Errorf("can't read user-data-file: %v", err)
111+
}
112+
dataStr := string(data)
113+
input.UserData = &dataStr
114+
}
115+
if pflags.Changed("user-data") {
116+
input.UserData = &flags.UserData
117+
}
33118

34-
func (c *SBMCreateMgr) Create(ctx context.Context, client *serverscom.Client, input any) (any, error) {
35-
sbmInput, ok := input.(*serverscom.SBMServerCreateInput)
36-
if !ok {
37-
return nil, fmt.Errorf("invalid input type for SBM server")
119+
if pflags.Changed("labels") {
120+
for i := range input.Hosts {
121+
maps.Copy(input.Hosts[i].Labels, flags.Labels)
122+
}
38123
}
39-
return client.Hosts.CreateSBMServers(ctx, *sbmInput)
124+
return nil
40125
}
41126

42-
func (c *SBMCreateMgr) NewCreateInput() any {
43-
return &serverscom.SBMServerCreateInput{}
127+
func newAddDSCmd(cmdContext *base.CmdContext) *cobra.Command {
128+
flags := &AddDSFlags{}
129+
130+
cmd := &cobra.Command{
131+
Use: "add",
132+
Short: "Create a dedicated server",
133+
Args: cobra.ArbitraryArgs,
134+
RunE: func(cmd *cobra.Command, args []string) error {
135+
136+
manager := cmdContext.GetManager()
137+
ctx, cancel := base.SetupContext(cmd, manager)
138+
defer cancel()
139+
140+
base.SetupProxy(cmd, manager)
141+
142+
input := serverscom.DedicatedServerCreateInput{}
143+
144+
if flags.InputPath != "" {
145+
if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), &input); err != nil {
146+
return err
147+
}
148+
}
149+
150+
if len(input.Hosts) == 0 && len(args) == 0 {
151+
return fmt.Errorf("no hosts found from positional args and no hosts found from input, can't continue")
152+
}
153+
154+
for _, hostname := range args {
155+
input.Hosts = append(input.Hosts, serverscom.DedicatedServerHostInput{
156+
Hostname: hostname,
157+
Labels: make(map[string]string),
158+
})
159+
}
160+
161+
err := applyFlagsToInput(&input, flags, cmd.Flags())
162+
if err != nil {
163+
return err
164+
}
165+
166+
scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient()
167+
168+
server, err := scClient.Hosts.CreateDedicatedServers(ctx, input)
169+
if err != nil {
170+
return err
171+
}
172+
173+
if server != nil {
174+
formatter := cmdContext.GetOrCreateFormatter(cmd)
175+
return formatter.Format(server)
176+
}
177+
178+
return nil
179+
},
180+
}
181+
182+
cmd.Flags().StringVarP(&flags.InputPath, "input", "i", "", "path to input file or '-' to read from stdin")
183+
184+
cmd.Flags().IntVar(&flags.LocationID, "location-id", 0, "Create the server(s) in the specific location ID")
185+
cmd.Flags().IntVar(&flags.ServerModelID, "server-model-id", 0, "Use specific server model ID to create the server")
186+
cmd.Flags().IntVar(&flags.OperatingSystemID, "operating-system-id", 0, "Install the specific operating system")
187+
cmd.Flags().StringSliceVar(&flags.Features, "feature", nil, "Set of features")
188+
cmd.Flags().IntVar(&flags.RAMSize, "ram-size", 0, "Desired amount of RAM in GB")
189+
cmd.Flags().IntVar(&flags.PublicUplinkID, "public-uplink-id", 0, "The public uplink ID, can be omitted if do not want public uplink")
190+
cmd.Flags().IntVar(&flags.PrivateUplinkID, "private-uplink-id", 0, "The private uplink ID")
191+
cmd.Flags().IntVar(&flags.PublicBandwidthID, "public-bandwidth-id", 0, "The public bandwidth ID, MUST be omitted if public uplink id is not passed")
192+
cmd.Flags().StringToIntVar(&flags.DriveSlots, "drive-slots", nil, "mapping of the specific slot to the specific drive model")
193+
cmd.Flags().StringArrayVar(&flags.Layout, "layout", nil, "Configuration of drives layout")
194+
cmd.Flags().StringArrayVar(&flags.Partitions, "partition", nil, "Configuration of the specific partitions")
195+
cmd.Flags().BoolVar(&flags.IPv6, "ipv6", false, "Enable IPv6")
196+
cmd.Flags().StringVar(&flags.UserDataFile, "user-data-file", "", "Path to user data which should be readed")
197+
cmd.Flags().StringVar(&flags.UserData, "user-data", "", "Content of user data")
198+
cmd.Flags().StringToStringVar(&flags.Labels, "labels", nil, "The set of labels which will be applied to the all hosts of this operation")
199+
200+
return cmd
44201
}
45202

46-
func newAddCmd(cmdContext *base.CmdContext, hostType *HostTypeCmd) *cobra.Command {
203+
func newAddSBMCmd(cmdContext *base.CmdContext) *cobra.Command {
47204
var path string
48205
cmd := &cobra.Command{
49206
Use: "add --input <path>",
50-
Short: fmt.Sprintf("Create a %s", hostType.entityName),
207+
Short: "Create an SBM server",
51208
Args: cobra.ExactArgs(0),
52209
RunE: func(cmd *cobra.Command, args []string) error {
53210
manager := cmdContext.GetManager()
@@ -56,15 +213,15 @@ func newAddCmd(cmdContext *base.CmdContext, hostType *HostTypeCmd) *cobra.Comman
56213

57214
base.SetupProxy(cmd, manager)
58215

59-
input := hostType.managers.createMgr.NewCreateInput()
216+
input := serverscom.SBMServerCreateInput{}
60217

61-
if err := base.ReadInputJSON(path, cmd.InOrStdin(), input); err != nil {
218+
if err := base.ReadInputJSON(path, cmd.InOrStdin(), &input); err != nil {
62219
return err
63220
}
64221

65222
scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient()
66223

67-
server, err := hostType.managers.createMgr.Create(ctx, scClient, input)
224+
server, err := scClient.Hosts.CreateSBMServers(ctx, input)
68225
if err != nil {
69226
return err
70227
}

cmd/entities/hosts/hosts.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ type HostTypeCmd struct {
1919
}
2020

2121
type HostManagers struct {
22-
getMgr HostGetter
23-
createMgr HostCreator
22+
getMgr HostGetter
2423
// for update we use simple commands in sake of simplicity
2524
powerMgr HostPowerer
2625
reinstallMgr HostReinstaller
@@ -40,11 +39,11 @@ func NewCmd(cmdContext *base.CmdContext) *cobra.Command {
4039
typeFlag: "dedicated_server",
4140
managers: HostManagers{
4241
getMgr: &DSGetMgr{},
43-
createMgr: &DSCreateMgr{},
4442
powerMgr: &DSPowerMgr{},
4543
reinstallMgr: &DSReinstallMgr{},
4644
},
4745
extraCmds: []func(*base.CmdContext) *cobra.Command{
46+
newAddDSCmd,
4847
newUpdateDSCmd,
4948
newListDSDriveSlotsCmd,
5049
newListDSConnectionsCmd,
@@ -73,11 +72,11 @@ func NewCmd(cmdContext *base.CmdContext) *cobra.Command {
7372
typeFlag: "sbm_server",
7473
managers: HostManagers{
7574
getMgr: &SBMGetMgr{},
76-
createMgr: &SBMCreateMgr{},
7775
powerMgr: &SBMPowerMgr{},
7876
reinstallMgr: &SBMReinstallMgr{},
7977
},
8078
extraCmds: []func(*base.CmdContext) *cobra.Command{
79+
newAddSBMCmd,
8180
newUpdateSBMCmd,
8281
newSBMReleaseCmd,
8382
},
@@ -119,9 +118,6 @@ func newHostTypeCmd(cmdContext *base.CmdContext, hostTypeCmd HostTypeCmd) *cobra
119118
hostCmd.AddCommand(newListCmd(cmdContext, &hostTypeCmd))
120119
hostCmd.AddCommand(newGetCmd(cmdContext, &hostTypeCmd))
121120

122-
if hostTypeCmd.managers.createMgr != nil {
123-
hostCmd.AddCommand(newAddCmd(cmdContext, &hostTypeCmd))
124-
}
125121
if hostTypeCmd.managers.powerMgr != nil {
126122
hostCmd.AddCommand(newPowerCmd(cmdContext, &hostTypeCmd))
127123
hostCmd.AddCommand(newListPowerFeedsCmd(cmdContext, &hostTypeCmd))

0 commit comments

Comments
 (0)