Skip to content

Commit 9fb030e

Browse files
authored
Merge pull request #38 from serverscom/add-skeleton-parameter
Add skeleton parameter to provide command input params
2 parents 047274a + 588fc68 commit 9fb030e

178 files changed

Lines changed: 789 additions & 267 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cmd/entities/cloud-instances/add.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,41 @@ import (
77
)
88

99
type AddedFlags struct {
10+
Skeleton bool
1011
InputPath string
1112
}
1213

1314
func newAddCmd(cmdContext *base.CmdContext) *cobra.Command {
1415
flags := &AddedFlags{}
1516

1617
cmd := &cobra.Command{
17-
Use: "add --input <path>",
18+
Use: "add",
1819
Short: "Add cloud instance",
1920
Args: cobra.ExactArgs(0),
2021
RunE: func(cmd *cobra.Command, args []string) error {
22+
formatter := cmdContext.GetOrCreateFormatter(cmd)
23+
24+
if flags.Skeleton {
25+
return formatter.FormatSkeleton("cloud-instances/add.json")
26+
}
27+
2128
manager := cmdContext.GetManager()
2229
ctx, cancel := base.SetupContext(cmd, manager)
2330
defer cancel()
2431

2532
base.SetupProxy(cmd, manager)
2633

2734
input := serverscom.CloudComputingInstanceCreateInput{}
28-
if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), &input); err != nil {
29-
return err
35+
36+
if flags.InputPath != "" {
37+
if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), &input); err != nil {
38+
return err
39+
}
40+
} else {
41+
required := []string{"input"}
42+
if err := base.ValidateFlags(cmd, required); err != nil {
43+
return err
44+
}
3045
}
3146

3247
scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient()
@@ -36,15 +51,14 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command {
3651
}
3752

3853
if out != nil {
39-
formatter := cmdContext.GetOrCreateFormatter(cmd)
4054
return formatter.Format(out)
4155
}
4256
return nil
4357
},
4458
}
4559

4660
cmd.Flags().StringVarP(&flags.InputPath, "input", "i", "", "path to input file or '-' to read from stdin")
47-
_ = cmd.MarkFlagRequired("input")
61+
cmd.Flags().BoolVarP(&flags.Skeleton, "skeleton", "s", false, "JSON object with structure that is required to be passed")
4862

4963
return cmd
5064
}

cmd/entities/cloud-instances/cloud_instances_test.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ import (
1414
)
1515

1616
var (
17-
fixtureBasePath = filepath.Join("..", "..", "..", "testdata", "entities", "cloud-instances")
18-
fixedTime = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC)
19-
testCloudInstanceID = "test-instance-id"
20-
testCloudInstance = serverscom.CloudComputingInstance{
17+
fixtureBasePath = filepath.Join("..", "..", "..", "testdata", "entities", "cloud-instances")
18+
skeletonTemplatePath = filepath.Join("..", "..", "..", "internal", "output", "skeletons", "templates", "cloud-instances")
19+
fixedTime = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC)
20+
testCloudInstanceID = "test-instance-id"
21+
testCloudInstance = serverscom.CloudComputingInstance{
2122
ID: testCloudInstanceID,
2223
Name: "test-instance",
2324
RegionID: 1,
@@ -308,6 +309,17 @@ func TestAddCloudInstancesCmd(t *testing.T) {
308309
Return(&testCloudInstance, nil)
309310
},
310311
},
312+
{
313+
name: "skeleton for cloud instance input",
314+
output: "json",
315+
args: []string{"--skeleton"},
316+
expectedOutput: testutils.ReadFixture(filepath.Join(skeletonTemplatePath, "add.json")),
317+
configureMock: func(mock *mocks.MockCloudComputingInstancesService) {
318+
mock.EXPECT().
319+
Create(gomock.Any(), gomock.Any()).
320+
Times(0)
321+
},
322+
},
311323
{
312324
name: "create cloud instance with error",
313325
expectError: true,
@@ -353,7 +365,7 @@ func TestAddCloudInstancesCmd(t *testing.T) {
353365
g.Expect(err).To(HaveOccurred())
354366
} else {
355367
g.Expect(err).To(BeNil())
356-
g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput)))
368+
g.Expect(builder.GetOutput()).To(MatchJSON(tc.expectedOutput))
357369
}
358370
})
359371
}

cmd/entities/hosts/add.go

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ package hosts
22

33
import (
44
"fmt"
5-
"log"
6-
"os"
7-
85
"maps"
6+
"os"
97

108
serverscom "github.com/serverscom/serverscom-go-client/pkg"
119
"github.com/serverscom/srvctl/cmd/base"
@@ -14,6 +12,7 @@ import (
1412
)
1513

1614
type AddDSFlags struct {
15+
Skeleton bool
1716
InputPath string
1817
LocationID int
1918
ServerModelID int
@@ -32,6 +31,11 @@ type AddDSFlags struct {
3231
Labels map[string]string
3332
}
3433

34+
type AddSBMFlags struct {
35+
Skeleton bool
36+
InputPath string
37+
}
38+
3539
func applyFlagsToInput(
3640
input *serverscom.DedicatedServerCreateInput,
3741
flags *AddDSFlags,
@@ -132,6 +136,11 @@ func newAddDSCmd(cmdContext *base.CmdContext) *cobra.Command {
132136
Short: "Create a dedicated server",
133137
Args: cobra.ArbitraryArgs,
134138
RunE: func(cmd *cobra.Command, args []string) error {
139+
formatter := cmdContext.GetOrCreateFormatter(cmd)
140+
141+
if flags.Skeleton {
142+
return formatter.FormatSkeleton("hosts/add_ds.json")
143+
}
135144

136145
manager := cmdContext.GetManager()
137146
ctx, cancel := base.SetupContext(cmd, manager)
@@ -145,6 +154,11 @@ func newAddDSCmd(cmdContext *base.CmdContext) *cobra.Command {
145154
if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), &input); err != nil {
146155
return err
147156
}
157+
} else {
158+
required := []string{"input"}
159+
if err := base.ValidateFlags(cmd, required); err != nil {
160+
return err
161+
}
148162
}
149163

150164
if len(input.Hosts) == 0 && len(args) == 0 {
@@ -171,7 +185,6 @@ func newAddDSCmd(cmdContext *base.CmdContext) *cobra.Command {
171185
}
172186

173187
if server != nil {
174-
formatter := cmdContext.GetOrCreateFormatter(cmd)
175188
return formatter.Format(server)
176189
}
177190

@@ -180,6 +193,7 @@ func newAddDSCmd(cmdContext *base.CmdContext) *cobra.Command {
180193
}
181194

182195
cmd.Flags().StringVarP(&flags.InputPath, "input", "i", "", "path to input file or '-' to read from stdin")
196+
cmd.Flags().BoolVarP(&flags.Skeleton, "skeleton", "s", false, "JSON object with structure that is required to be passed")
183197

184198
cmd.Flags().IntVar(&flags.LocationID, "location-id", 0, "Create the server(s) in the specific location ID")
185199
cmd.Flags().IntVar(&flags.ServerModelID, "server-model-id", 0, "Use specific server model ID to create the server")
@@ -201,12 +215,19 @@ func newAddDSCmd(cmdContext *base.CmdContext) *cobra.Command {
201215
}
202216

203217
func newAddSBMCmd(cmdContext *base.CmdContext) *cobra.Command {
204-
var path string
218+
flags := &AddSBMFlags{}
219+
205220
cmd := &cobra.Command{
206221
Use: "add --input <path>",
207222
Short: "Create an SBM server",
208223
Args: cobra.ExactArgs(0),
209224
RunE: func(cmd *cobra.Command, args []string) error {
225+
formatter := cmdContext.GetOrCreateFormatter(cmd)
226+
227+
if flags.Skeleton {
228+
return formatter.FormatSkeleton("hosts/add_sbm.json")
229+
}
230+
210231
manager := cmdContext.GetManager()
211232
ctx, cancel := base.SetupContext(cmd, manager)
212233
defer cancel()
@@ -215,8 +236,15 @@ func newAddSBMCmd(cmdContext *base.CmdContext) *cobra.Command {
215236

216237
input := serverscom.SBMServerCreateInput{}
217238

218-
if err := base.ReadInputJSON(path, cmd.InOrStdin(), &input); err != nil {
219-
return err
239+
if flags.InputPath != "" {
240+
if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), &input); err != nil {
241+
return err
242+
}
243+
} else {
244+
required := []string{"input"}
245+
if err := base.ValidateFlags(cmd, required); err != nil {
246+
return err
247+
}
220248
}
221249

222250
scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient()
@@ -227,18 +255,15 @@ func newAddSBMCmd(cmdContext *base.CmdContext) *cobra.Command {
227255
}
228256

229257
if server != nil {
230-
formatter := cmdContext.GetOrCreateFormatter(cmd)
231258
return formatter.Format(server)
232259
}
233260

234261
return nil
235262
},
236263
}
237264

238-
cmd.Flags().StringVarP(&path, "input", "i", "", "path to input file or '-' to read from stdin")
239-
if err := cmd.MarkFlagRequired("input"); err != nil {
240-
log.Fatal(err)
241-
}
265+
cmd.Flags().StringVarP(&flags.InputPath, "input", "i", "", "path to input file or '-' to read from stdin")
266+
cmd.Flags().BoolVarP(&flags.Skeleton, "skeleton", "s", false, "JSON object with structure that is required to be passed")
242267

243268
return cmd
244269
}

cmd/entities/hosts/hosts_test.go

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@ import (
1515
)
1616

1717
var (
18-
testId = "testId"
19-
testNetworkId = "testNetId"
20-
fixtureBasePath = filepath.Join("..", "..", "..", "testdata", "entities", "hosts")
21-
fixedTime = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC)
22-
testPublicIP = "1.2.3.4"
23-
testLocationCode = "test"
24-
testHost = serverscom.Host{
18+
testId = "testId"
19+
testNetworkId = "testNetId"
20+
fixtureBasePath = filepath.Join("..", "..", "..", "testdata", "entities", "hosts")
21+
skeletonTemplatePath = filepath.Join("..", "..", "..", "internal", "output", "skeletons", "templates", "hosts")
22+
fixedTime = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC)
23+
testPublicIP = "1.2.3.4"
24+
testLocationCode = "test"
25+
testHost = serverscom.Host{
2526
ID: testId,
2627
Title: "example.aa",
2728
Status: "active",
@@ -340,6 +341,17 @@ func TestAddDSCmd(t *testing.T) {
340341
Return([]serverscom.DedicatedServer{testDS}, nil)
341342
},
342343
},
344+
{
345+
name: "skeleton for dedicated server input",
346+
output: "json",
347+
args: []string{"--skeleton"},
348+
expectedOutput: testutils.ReadFixture(filepath.Join(skeletonTemplatePath, "add_ds.json")),
349+
configureMock: func(mock *mocks.MockHostsService) {
350+
mock.EXPECT().
351+
CreateDedicatedServers(gomock.Any(), gomock.Any()).
352+
Times(0)
353+
},
354+
},
343355
{
344356
name: "create dedicated server with error",
345357
expectError: true,
@@ -385,7 +397,7 @@ func TestAddDSCmd(t *testing.T) {
385397
g.Expect(err).To(HaveOccurred())
386398
} else {
387399
g.Expect(err).To(BeNil())
388-
g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput)))
400+
g.Expect(builder.GetOutput()).To(MatchJSON(tc.expectedOutput))
389401
}
390402
})
391403
}
@@ -424,6 +436,17 @@ func TestAddSBMCmd(t *testing.T) {
424436
Return([]serverscom.SBMServer{testSBM}, nil)
425437
},
426438
},
439+
{
440+
name: "skeleton for SBM server input",
441+
output: "json",
442+
args: []string{"--skeleton"},
443+
expectedOutput: testutils.ReadFixture(filepath.Join(skeletonTemplatePath, "add_sbm.json")),
444+
configureMock: func(mock *mocks.MockHostsService) {
445+
mock.EXPECT().
446+
CreateSBMServers(gomock.Any(), gomock.Any()).
447+
Times(0)
448+
},
449+
},
427450
{
428451
name: "create SBM server with error",
429452
expectError: true,
@@ -469,7 +492,7 @@ func TestAddSBMCmd(t *testing.T) {
469492
g.Expect(err).To(HaveOccurred())
470493
} else {
471494
g.Expect(err).To(BeNil())
472-
g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput)))
495+
g.Expect(builder.GetOutput()).To(MatchJSON(tc.expectedOutput))
473496
}
474497
})
475498
}

cmd/entities/hosts/reinstall.go

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@ package hosts
33
import (
44
"context"
55
"fmt"
6-
"log"
7-
86
serverscom "github.com/serverscom/serverscom-go-client/pkg"
97
"github.com/serverscom/srvctl/cmd/base"
108
"github.com/spf13/cobra"
119
)
1210

11+
type AddedFlags struct {
12+
Skeleton bool
13+
InputPath string
14+
}
15+
1316
type HostReinstaller interface {
1417
Reinstall(ctx context.Context, client *serverscom.Client, id string, input any) (any, error)
1518
NewReinstallInput() any
@@ -44,22 +47,35 @@ func (c *SBMReinstallMgr) NewReinstallInput() any {
4447
}
4548

4649
func newReinstallCmd(cmdContext *base.CmdContext, hostType *HostTypeCmd) *cobra.Command {
47-
var path string
50+
flags := &AddedFlags{}
4851

4952
cmd := &cobra.Command{
5053
Use: "reinstall <id>",
5154
Short: fmt.Sprintf("Reinstall OS for a %s", hostType.entityName),
5255
Args: cobra.ExactArgs(1),
5356
RunE: func(cmd *cobra.Command, args []string) error {
57+
formatter := cmdContext.GetOrCreateFormatter(cmd)
58+
59+
if flags.Skeleton {
60+
return formatter.FormatSkeleton("hosts/reinstall.json")
61+
}
62+
5463
manager := cmdContext.GetManager()
5564
ctx, cancel := base.SetupContext(cmd, manager)
5665
defer cancel()
5766
base.SetupProxy(cmd, manager)
5867

5968
input := hostType.managers.reinstallMgr.NewReinstallInput()
6069

61-
if err := base.ReadInputJSON(path, cmd.InOrStdin(), input); err != nil {
62-
return err
70+
if flags.InputPath != "" {
71+
if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), &input); err != nil {
72+
return err
73+
}
74+
} else {
75+
required := []string{"input"}
76+
if err := base.ValidateFlags(cmd, required); err != nil {
77+
return err
78+
}
6379
}
6480

6581
scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient()
@@ -71,17 +87,14 @@ func newReinstallCmd(cmdContext *base.CmdContext, hostType *HostTypeCmd) *cobra.
7187
}
7288

7389
if server != nil {
74-
formatter := cmdContext.GetOrCreateFormatter(cmd)
7590
return formatter.Format(server)
7691
}
7792
return nil
7893
},
7994
}
8095

81-
cmd.Flags().StringVarP(&path, "input", "i", "", "path to input file or '-' to read from stdin")
82-
if err := cmd.MarkFlagRequired("input"); err != nil {
83-
log.Fatal(err)
84-
}
96+
cmd.Flags().StringVarP(&flags.InputPath, "input", "i", "", "path to input file or '-' to read from stdin")
97+
cmd.Flags().BoolVarP(&flags.Skeleton, "skeleton", "s", false, "JSON object with structure that is required to be passed")
8598

8699
return cmd
87100
}

0 commit comments

Comments
 (0)