From ffbde50cd0a344716ad04779973d9e3423a808cb Mon Sep 17 00:00:00 2001 From: Anton Bozhiy Date: Mon, 19 Jan 2026 17:17:30 +0200 Subject: [PATCH 01/17] implement skeleton flag for ssh keys - in progress --- cmd/base/utils.go | 61 ++++++++++++++++++++++++ cmd/entities/ssh-keys/add.go | 22 +++++++-- cmd/entities/ssh-keys/ssh_keys_test.go | 11 +++++ testdata/entities/ssh-keys/skeleton.json | 4 ++ 4 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 testdata/entities/ssh-keys/skeleton.json diff --git a/cmd/base/utils.go b/cmd/base/utils.go index be8c7af..4856363 100644 --- a/cmd/base/utils.go +++ b/cmd/base/utils.go @@ -7,6 +7,7 @@ import ( "io" "log" "os" + "reflect" "runtime" "strings" "time" @@ -213,3 +214,63 @@ func ValidateFlags(cmd *cobra.Command, required []string) error { return nil } + +func NewValidateFlagsFn[T any](cmd *cobra.Command, input *T) error { + t := reflect.TypeOf(*input) + if t.Kind() != reflect.Struct { + return fmt.Errorf("pointer to struct is expected") + } + + required := make([]string, 0, t.NumField()) + var missing []string + + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + + if field.Tag.Get("required") == "true" { + flag := field.Tag.Get("flag") + if flag == "" || flag == "-" { + continue + } + + required = append(required, flag) + } + } + + for _, f := range required { + if !cmd.Flags().Changed(f) { + missing = append(missing, "--"+f) + } + } + + if len(missing) > 0 { + return fmt.Errorf( + "provide all required flags (missing: %s)", + strings.Join(missing, ", "), + ) + } + + return nil +} + +func RequiredFieldsMap[T any](input *T) (map[string]any, error) { + t := reflect.TypeOf(*input) + if t.Kind() != reflect.Struct { + return nil, fmt.Errorf("pointer to struct is expected") + } + + result := make(map[string]any) + + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + + if field.Tag.Get("required") != "true" { + continue + } + + n := field.Tag.Get("json") + result[n] = reflect.Zero(field.Type).Interface() + } + + return result, nil +} diff --git a/cmd/entities/ssh-keys/add.go b/cmd/entities/ssh-keys/add.go index 696a39c..b743719 100644 --- a/cmd/entities/ssh-keys/add.go +++ b/cmd/entities/ssh-keys/add.go @@ -7,22 +7,30 @@ import ( ) type AddedFlags struct { + Skeleton bool InputPath string Name string PublicKey string Labels []string } +type TmpInput struct { + Name string `json:"name" flag:"name" required:"true"` + PublicKey string `json:"public_key" flag:"public-key" required:"true"` + Labels map[string]string `json:"labels,omitempty" flag:"label"` +} + func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { flags := &AddedFlags{} cmd := &cobra.Command{ - Use: "add --input ", + Use: "add", Short: "Add an ssh key", Long: "Add a new SSH key to account", Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { manager := cmdContext.GetManager() + formatter := cmdContext.GetOrCreateFormatter(cmd) ctx, cancel := base.SetupContext(cmd, manager) defer cancel() @@ -35,9 +43,15 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), input); err != nil { return err } + } else if flags.Skeleton { + requiredMap, err := base.RequiredFieldsMap(&TmpInput{}) + if err != nil { + return err + } + + return formatter.Format(requiredMap) } else { - required := []string{"name", "public-key"} - if err := base.ValidateFlags(cmd, required); err != nil { + if err := base.NewValidateFlagsFn(cmd, &TmpInput{}); err != nil { return err } } @@ -53,13 +67,13 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { } if sshKey != nil { - formatter := cmdContext.GetOrCreateFormatter(cmd) return formatter.Format(sshKey) } return nil }, } + cmd.Flags().BoolVarP(&flags.Skeleton, "skeleton", "s", false, "JSON object with structure that is required to be passed") cmd.Flags().StringVarP(&flags.InputPath, "input", "i", "", "path to input file or '-' to read from stdin") cmd.Flags().StringVarP(&flags.Name, "name", "n", "", "A name of a SSH key") cmd.Flags().StringVarP(&flags.PublicKey, "public-key", "", "", "A public-key of a SSH key") diff --git a/cmd/entities/ssh-keys/ssh_keys_test.go b/cmd/entities/ssh-keys/ssh_keys_test.go index 2afd470..f8a9f06 100644 --- a/cmd/entities/ssh-keys/ssh_keys_test.go +++ b/cmd/entities/ssh-keys/ssh_keys_test.go @@ -69,6 +69,17 @@ func TestAddSSHKeysCmd(t *testing.T) { Return(&testSSHKey, nil) }, }, + { + name: "skeleton for ssh key input", + output: "json", + args: []string{"--skeleton"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "skeleton.json")), + configureMock: func(mock *mocks.MockSSHKeysService) { + mock.EXPECT(). + Create(gomock.Any(), gomock.Any()). + Times(0) + }, + }, { name: "create ssh key with error", expectError: true, diff --git a/testdata/entities/ssh-keys/skeleton.json b/testdata/entities/ssh-keys/skeleton.json new file mode 100644 index 0000000..cadb3c8 --- /dev/null +++ b/testdata/entities/ssh-keys/skeleton.json @@ -0,0 +1,4 @@ +{ + "name": "", + "public_key": "" +} \ No newline at end of file From afbbbf3663a0a9002d68f7db862833c025ef78d3 Mon Sep 17 00:00:00 2001 From: Anton Bozhiy Date: Mon, 19 Jan 2026 17:55:39 +0200 Subject: [PATCH 02/17] rm NewValidateFlagsFn --- cmd/base/utils.go | 38 ------------------------------------ cmd/entities/ssh-keys/add.go | 8 ++++---- 2 files changed, 4 insertions(+), 42 deletions(-) diff --git a/cmd/base/utils.go b/cmd/base/utils.go index 4856363..0d47bbd 100644 --- a/cmd/base/utils.go +++ b/cmd/base/utils.go @@ -215,44 +215,6 @@ func ValidateFlags(cmd *cobra.Command, required []string) error { return nil } -func NewValidateFlagsFn[T any](cmd *cobra.Command, input *T) error { - t := reflect.TypeOf(*input) - if t.Kind() != reflect.Struct { - return fmt.Errorf("pointer to struct is expected") - } - - required := make([]string, 0, t.NumField()) - var missing []string - - for i := 0; i < t.NumField(); i++ { - field := t.Field(i) - - if field.Tag.Get("required") == "true" { - flag := field.Tag.Get("flag") - if flag == "" || flag == "-" { - continue - } - - required = append(required, flag) - } - } - - for _, f := range required { - if !cmd.Flags().Changed(f) { - missing = append(missing, "--"+f) - } - } - - if len(missing) > 0 { - return fmt.Errorf( - "provide all required flags (missing: %s)", - strings.Join(missing, ", "), - ) - } - - return nil -} - func RequiredFieldsMap[T any](input *T) (map[string]any, error) { t := reflect.TypeOf(*input) if t.Kind() != reflect.Struct { diff --git a/cmd/entities/ssh-keys/add.go b/cmd/entities/ssh-keys/add.go index b743719..1483881 100644 --- a/cmd/entities/ssh-keys/add.go +++ b/cmd/entities/ssh-keys/add.go @@ -15,9 +15,9 @@ type AddedFlags struct { } type TmpInput struct { - Name string `json:"name" flag:"name" required:"true"` - PublicKey string `json:"public_key" flag:"public-key" required:"true"` - Labels map[string]string `json:"labels,omitempty" flag:"label"` + Name string `json:"name" required:"true"` + PublicKey string `json:"public_key" required:"true"` + Labels map[string]string `json:"labels,omitempty"` } func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { @@ -51,7 +51,7 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { return formatter.Format(requiredMap) } else { - if err := base.NewValidateFlagsFn(cmd, &TmpInput{}); err != nil { + if err := base.ValidateFlags(cmd, []string{"name", "public-key"}); err != nil { return err } } From 673e8aeae57cf485dc6f465c47f040bdcd045689 Mon Sep 17 00:00:00 2001 From: Anton Bozhiy Date: Wed, 21 Jan 2026 16:58:41 +0200 Subject: [PATCH 03/17] implement skeleton flag for add ssh keys cmd --- cmd/entities/ssh-keys/add.go | 12 ++++++++++-- cmd/entities/ssh-keys/ssh_keys_test.go | 11 ++++++----- .../skeleton-templates/ssh-keys/add.json | 0 internal/output/skeletons/skeletons.go | 19 +++++++++++++++++++ 4 files changed, 35 insertions(+), 7 deletions(-) rename testdata/entities/ssh-keys/skeleton.json => internal/output/skeletons/skeleton-templates/ssh-keys/add.json (100%) create mode 100644 internal/output/skeletons/skeletons.go diff --git a/cmd/entities/ssh-keys/add.go b/cmd/entities/ssh-keys/add.go index 1483881..6fce45a 100644 --- a/cmd/entities/ssh-keys/add.go +++ b/cmd/entities/ssh-keys/add.go @@ -1,9 +1,12 @@ package sshkeys import ( + "encoding/json" serverscom "github.com/serverscom/serverscom-go-client/pkg" "github.com/serverscom/srvctl/cmd/base" + "github.com/serverscom/srvctl/internal/output/skeletons" "github.com/spf13/cobra" + "io/fs" ) type AddedFlags struct { @@ -44,12 +47,17 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { return err } } else if flags.Skeleton { - requiredMap, err := base.RequiredFieldsMap(&TmpInput{}) + raw, err := fs.ReadFile(skeletons.FS, "ssh-keys/add.json") if err != nil { return err } - return formatter.Format(requiredMap) + var data map[string]any + if err := json.Unmarshal(raw, &data); err != nil { + return err + } + + return formatter.Format(data) } else { if err := base.ValidateFlags(cmd, []string{"name", "public-key"}); err != nil { return err diff --git a/cmd/entities/ssh-keys/ssh_keys_test.go b/cmd/entities/ssh-keys/ssh_keys_test.go index f8a9f06..9e81b7f 100644 --- a/cmd/entities/ssh-keys/ssh_keys_test.go +++ b/cmd/entities/ssh-keys/ssh_keys_test.go @@ -14,10 +14,11 @@ import ( ) var ( - testFingerprint = "00:11:22:33:44:55:66:77:88:99" - fixtureBasePath = filepath.Join("..", "..", "..", "testdata", "entities", "ssh-keys") - fixedTime = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC) - testSSHKey = serverscom.SSHKey{ + testFingerprint = "00:11:22:33:44:55:66:77:88:99" + fixtureBasePath = filepath.Join("..", "..", "..", "testdata", "entities", "ssh-keys") + skeletonTemplatePath = filepath.Join("..", "..", "..", "internal", "output", "skeletons", "skeleton-templates", "ssh-keys") + fixedTime = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC) + testSSHKey = serverscom.SSHKey{ Name: "test-key", Fingerprint: testFingerprint, Labels: map[string]string{"foo": "bar"}, @@ -73,7 +74,7 @@ func TestAddSSHKeysCmd(t *testing.T) { name: "skeleton for ssh key input", output: "json", args: []string{"--skeleton"}, - expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "skeleton.json")), + expectedOutput: testutils.ReadFixture(filepath.Join(skeletonTemplatePath, "add.json")), configureMock: func(mock *mocks.MockSSHKeysService) { mock.EXPECT(). Create(gomock.Any(), gomock.Any()). diff --git a/testdata/entities/ssh-keys/skeleton.json b/internal/output/skeletons/skeleton-templates/ssh-keys/add.json similarity index 100% rename from testdata/entities/ssh-keys/skeleton.json rename to internal/output/skeletons/skeleton-templates/ssh-keys/add.json diff --git a/internal/output/skeletons/skeletons.go b/internal/output/skeletons/skeletons.go new file mode 100644 index 0000000..7ee5218 --- /dev/null +++ b/internal/output/skeletons/skeletons.go @@ -0,0 +1,19 @@ +package skeletons + +import ( + "embed" + "io/fs" +) + +//go:embed skeleton-templates/** +var RootFS embed.FS +var FS fs.FS + +func init() { + var err error + + FS, err = fs.Sub(RootFS, "skeleton-templates") + if err != nil { + panic(err) + } +} From b620448ad19994906f963e512533a72b171e2c16 Mon Sep 17 00:00:00 2001 From: Anton Bozhiy Date: Wed, 21 Jan 2026 17:00:29 +0200 Subject: [PATCH 04/17] rm RequiredFieldsMap fn --- cmd/base/utils.go | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/cmd/base/utils.go b/cmd/base/utils.go index 0d47bbd..be8c7af 100644 --- a/cmd/base/utils.go +++ b/cmd/base/utils.go @@ -7,7 +7,6 @@ import ( "io" "log" "os" - "reflect" "runtime" "strings" "time" @@ -214,25 +213,3 @@ func ValidateFlags(cmd *cobra.Command, required []string) error { return nil } - -func RequiredFieldsMap[T any](input *T) (map[string]any, error) { - t := reflect.TypeOf(*input) - if t.Kind() != reflect.Struct { - return nil, fmt.Errorf("pointer to struct is expected") - } - - result := make(map[string]any) - - for i := 0; i < t.NumField(); i++ { - field := t.Field(i) - - if field.Tag.Get("required") != "true" { - continue - } - - n := field.Tag.Get("json") - result[n] = reflect.Zero(field.Type).Interface() - } - - return result, nil -} From 6d4423ece5523d0d37a1bdb303ac1a817a7016cc Mon Sep 17 00:00:00 2001 From: Anton Bozhiy Date: Wed, 21 Jan 2026 17:01:49 +0200 Subject: [PATCH 05/17] rm TmpInput --- cmd/entities/ssh-keys/add.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cmd/entities/ssh-keys/add.go b/cmd/entities/ssh-keys/add.go index 6fce45a..89af072 100644 --- a/cmd/entities/ssh-keys/add.go +++ b/cmd/entities/ssh-keys/add.go @@ -17,12 +17,6 @@ type AddedFlags struct { Labels []string } -type TmpInput struct { - Name string `json:"name" required:"true"` - PublicKey string `json:"public_key" required:"true"` - Labels map[string]string `json:"labels,omitempty"` -} - func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { flags := &AddedFlags{} From 9180910f816a7cebb25bc3231d0910a0f1db79a8 Mon Sep 17 00:00:00 2001 From: Anton Bozhiy Date: Wed, 21 Jan 2026 18:37:33 +0200 Subject: [PATCH 06/17] refactoring --- cmd/entities/ssh-keys/add.go | 20 ++++--------- cmd/entities/ssh-keys/ssh_keys_test.go | 2 +- internal/output/formatter.go | 29 ++++++++++++++++--- .../output/skeletons/{skeletons.go => fs.go} | 4 +-- .../ssh-keys/add.json | 0 5 files changed, 33 insertions(+), 22 deletions(-) rename internal/output/skeletons/{skeletons.go => fs.go} (64%) rename internal/output/skeletons/{skeleton-templates => templates}/ssh-keys/add.json (100%) diff --git a/cmd/entities/ssh-keys/add.go b/cmd/entities/ssh-keys/add.go index 89af072..fd634ee 100644 --- a/cmd/entities/ssh-keys/add.go +++ b/cmd/entities/ssh-keys/add.go @@ -1,12 +1,10 @@ package sshkeys import ( - "encoding/json" + "fmt" serverscom "github.com/serverscom/serverscom-go-client/pkg" "github.com/serverscom/srvctl/cmd/base" - "github.com/serverscom/srvctl/internal/output/skeletons" "github.com/spf13/cobra" - "io/fs" ) type AddedFlags struct { @@ -41,19 +39,11 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { return err } } else if flags.Skeleton { - raw, err := fs.ReadFile(skeletons.FS, "ssh-keys/add.json") - if err != nil { - return err - } - - var data map[string]any - if err := json.Unmarshal(raw, &data); err != nil { - return err - } - - return formatter.Format(data) + tmplPath := fmt.Sprintf("%s/%s.json", cmd.Parent().Name(), cmd.Name()) + return formatter.FormatSkeleton(tmplPath) } else { - if err := base.ValidateFlags(cmd, []string{"name", "public-key"}); err != nil { + required := []string{"name", "public-key"} + if err := base.ValidateFlags(cmd, required); err != nil { return err } } diff --git a/cmd/entities/ssh-keys/ssh_keys_test.go b/cmd/entities/ssh-keys/ssh_keys_test.go index 9e81b7f..b228eed 100644 --- a/cmd/entities/ssh-keys/ssh_keys_test.go +++ b/cmd/entities/ssh-keys/ssh_keys_test.go @@ -16,7 +16,7 @@ import ( var ( testFingerprint = "00:11:22:33:44:55:66:77:88:99" fixtureBasePath = filepath.Join("..", "..", "..", "testdata", "entities", "ssh-keys") - skeletonTemplatePath = filepath.Join("..", "..", "..", "internal", "output", "skeletons", "skeleton-templates", "ssh-keys") + skeletonTemplatePath = filepath.Join("..", "..", "..", "internal", "output", "skeletons", "templates", "ssh-keys") fixedTime = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC) testSSHKey = serverscom.SSHKey{ Name: "test-key", diff --git a/internal/output/formatter.go b/internal/output/formatter.go index 1b7a2e4..0b271c5 100644 --- a/internal/output/formatter.go +++ b/internal/output/formatter.go @@ -2,12 +2,13 @@ package output import ( "encoding/json" - "html/template" - "io" - "github.com/serverscom/srvctl/internal/config" + "github.com/serverscom/srvctl/internal/output/skeletons" "github.com/spf13/cobra" "gopkg.in/yaml.v3" + "html/template" + "io" + "io/fs" ) // Formatter represents formatter struct with custom io.Writer @@ -74,7 +75,7 @@ func (f *Formatter) Format(v any) error { } } -// formatText formats data in text format +// FormatText formats data in text format func (f *Formatter) FormatText(v any) error { switch data := v.(type) { case ConfigInfo: @@ -83,3 +84,23 @@ func (f *Formatter) FormatText(v any) error { return f.formatText(data) } } + +// FormatSkeleton formats skeleton template according to format +func (f *Formatter) FormatSkeleton(v any) error { + switch path := v.(type) { + case string: + raw, err := fs.ReadFile(skeletons.FS, path) + if err != nil { + return err + } + + var data map[string]any + if err := json.Unmarshal(raw, &data); err != nil { + return err + } + + return f.Format(data) + default: + return f.Format(v) + } +} diff --git a/internal/output/skeletons/skeletons.go b/internal/output/skeletons/fs.go similarity index 64% rename from internal/output/skeletons/skeletons.go rename to internal/output/skeletons/fs.go index 7ee5218..43c398f 100644 --- a/internal/output/skeletons/skeletons.go +++ b/internal/output/skeletons/fs.go @@ -5,14 +5,14 @@ import ( "io/fs" ) -//go:embed skeleton-templates/** +//go:embed templates/** var RootFS embed.FS var FS fs.FS func init() { var err error - FS, err = fs.Sub(RootFS, "skeleton-templates") + FS, err = fs.Sub(RootFS, "templates") if err != nil { panic(err) } diff --git a/internal/output/skeletons/skeleton-templates/ssh-keys/add.json b/internal/output/skeletons/templates/ssh-keys/add.json similarity index 100% rename from internal/output/skeletons/skeleton-templates/ssh-keys/add.json rename to internal/output/skeletons/templates/ssh-keys/add.json From 86917aed13ca7e4344676243b19fccf9a0411611 Mon Sep 17 00:00:00 2001 From: Anton Bozhiy Date: Fri, 23 Jan 2026 15:14:29 +0200 Subject: [PATCH 07/17] add skeleton flag to ds/sbm servers cmd --- cmd/entities/hosts/add.go | 45 ++++++++++++----- cmd/entities/hosts/hosts_test.go | 41 +++++++++++---- cmd/entities/hosts/reinstall.go | 30 +++++++---- cmd/entities/ssh-keys/add.go | 3 +- cmd/entities/ssh-keys/ssh_keys_test.go | 2 +- .../skeletons/templates/hosts/add_ds.json | 50 +++++++++++++++++++ .../skeletons/templates/hosts/add_sbm.json | 14 ++++++ .../skeletons/templates/hosts/reinstall.json | 0 .../skeletons/templates/ssh-keys/add.json | 5 +- 9 files changed, 155 insertions(+), 35 deletions(-) create mode 100644 internal/output/skeletons/templates/hosts/add_ds.json create mode 100644 internal/output/skeletons/templates/hosts/add_sbm.json create mode 100644 internal/output/skeletons/templates/hosts/reinstall.json diff --git a/cmd/entities/hosts/add.go b/cmd/entities/hosts/add.go index 0339def..05009c9 100644 --- a/cmd/entities/hosts/add.go +++ b/cmd/entities/hosts/add.go @@ -2,10 +2,8 @@ package hosts import ( "fmt" - "log" - "os" - "maps" + "os" serverscom "github.com/serverscom/serverscom-go-client/pkg" "github.com/serverscom/srvctl/cmd/base" @@ -14,6 +12,7 @@ import ( ) type AddDSFlags struct { + Skeleton bool InputPath string LocationID int ServerModelID int @@ -32,6 +31,11 @@ type AddDSFlags struct { Labels map[string]string } +type AddSBMFlags struct { + Skeleton bool + InputPath string +} + func applyFlagsToInput( input *serverscom.DedicatedServerCreateInput, flags *AddDSFlags, @@ -132,7 +136,7 @@ func newAddDSCmd(cmdContext *base.CmdContext) *cobra.Command { Short: "Create a dedicated server", Args: cobra.ArbitraryArgs, RunE: func(cmd *cobra.Command, args []string) error { - + formatter := cmdContext.GetOrCreateFormatter(cmd) manager := cmdContext.GetManager() ctx, cancel := base.SetupContext(cmd, manager) defer cancel() @@ -145,6 +149,13 @@ func newAddDSCmd(cmdContext *base.CmdContext) *cobra.Command { if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), &input); err != nil { return err } + } else if flags.Skeleton { + return formatter.FormatSkeleton("hosts/add_ds.json") + } else { + required := []string{"input"} + if err := base.ValidateFlags(cmd, required); err != nil { + return err + } } if len(input.Hosts) == 0 && len(args) == 0 { @@ -171,7 +182,6 @@ func newAddDSCmd(cmdContext *base.CmdContext) *cobra.Command { } if server != nil { - formatter := cmdContext.GetOrCreateFormatter(cmd) return formatter.Format(server) } @@ -180,6 +190,7 @@ func newAddDSCmd(cmdContext *base.CmdContext) *cobra.Command { } cmd.Flags().StringVarP(&flags.InputPath, "input", "i", "", "path to input file or '-' to read from stdin") + cmd.Flags().BoolVarP(&flags.Skeleton, "skeleton", "s", false, "JSON object with structure that is required to be passed") cmd.Flags().IntVar(&flags.LocationID, "location-id", 0, "Create the server(s) in the specific location ID") cmd.Flags().IntVar(&flags.ServerModelID, "server-model-id", 0, "Use specific server model ID to create the server") @@ -201,12 +212,14 @@ func newAddDSCmd(cmdContext *base.CmdContext) *cobra.Command { } func newAddSBMCmd(cmdContext *base.CmdContext) *cobra.Command { - var path string + flags := &AddSBMFlags{} + cmd := &cobra.Command{ Use: "add --input ", Short: "Create an SBM server", Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { + formatter := cmdContext.GetOrCreateFormatter(cmd) manager := cmdContext.GetManager() ctx, cancel := base.SetupContext(cmd, manager) defer cancel() @@ -215,8 +228,17 @@ func newAddSBMCmd(cmdContext *base.CmdContext) *cobra.Command { input := serverscom.SBMServerCreateInput{} - if err := base.ReadInputJSON(path, cmd.InOrStdin(), &input); err != nil { - return err + if flags.InputPath != "" { + if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), &input); err != nil { + return err + } + } else if flags.Skeleton { + return formatter.FormatSkeleton("hosts/add_sbm.json") + } else { + required := []string{"input"} + if err := base.ValidateFlags(cmd, required); err != nil { + return err + } } scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() @@ -227,7 +249,6 @@ func newAddSBMCmd(cmdContext *base.CmdContext) *cobra.Command { } if server != nil { - formatter := cmdContext.GetOrCreateFormatter(cmd) return formatter.Format(server) } @@ -235,10 +256,8 @@ func newAddSBMCmd(cmdContext *base.CmdContext) *cobra.Command { }, } - cmd.Flags().StringVarP(&path, "input", "i", "", "path to input file or '-' to read from stdin") - if err := cmd.MarkFlagRequired("input"); err != nil { - log.Fatal(err) - } + cmd.Flags().StringVarP(&flags.InputPath, "input", "i", "", "path to input file or '-' to read from stdin") + cmd.Flags().BoolVarP(&flags.Skeleton, "skeleton", "s", false, "JSON object with structure that is required to be passed") return cmd } diff --git a/cmd/entities/hosts/hosts_test.go b/cmd/entities/hosts/hosts_test.go index e6e9923..590cd44 100644 --- a/cmd/entities/hosts/hosts_test.go +++ b/cmd/entities/hosts/hosts_test.go @@ -15,13 +15,14 @@ import ( ) var ( - testId = "testId" - testNetworkId = "testNetId" - fixtureBasePath = filepath.Join("..", "..", "..", "testdata", "entities", "hosts") - fixedTime = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC) - testPublicIP = "1.2.3.4" - testLocationCode = "test" - testHost = serverscom.Host{ + testId = "testId" + testNetworkId = "testNetId" + fixtureBasePath = filepath.Join("..", "..", "..", "testdata", "entities", "hosts") + skeletonTemplatePath = filepath.Join("..", "..", "..", "internal", "output", "skeletons", "templates", "hosts") + fixedTime = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC) + testPublicIP = "1.2.3.4" + testLocationCode = "test" + testHost = serverscom.Host{ ID: testId, Title: "example.aa", Status: "active", @@ -340,6 +341,17 @@ func TestAddDSCmd(t *testing.T) { Return([]serverscom.DedicatedServer{testDS}, nil) }, }, + { + name: "skeleton for dedicated server input", + output: "json", + args: []string{"--skeleton"}, + expectedOutput: testutils.ReadFixture(filepath.Join(skeletonTemplatePath, "add_ds.json")), + configureMock: func(mock *mocks.MockHostsService) { + mock.EXPECT(). + CreateDedicatedServers(gomock.Any(), gomock.Any()). + Times(0) + }, + }, { name: "create dedicated server with error", expectError: true, @@ -385,7 +397,7 @@ func TestAddDSCmd(t *testing.T) { g.Expect(err).To(HaveOccurred()) } else { g.Expect(err).To(BeNil()) - g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + g.Expect(builder.GetOutput()).To(MatchJSON(tc.expectedOutput)) } }) } @@ -424,6 +436,17 @@ func TestAddSBMCmd(t *testing.T) { Return([]serverscom.SBMServer{testSBM}, nil) }, }, + { + name: "skeleton for SBM server input", + output: "json", + args: []string{"--skeleton"}, + expectedOutput: testutils.ReadFixture(filepath.Join(skeletonTemplatePath, "add_sbm.json")), + configureMock: func(mock *mocks.MockHostsService) { + mock.EXPECT(). + CreateSBMServers(gomock.Any(), gomock.Any()). + Times(0) + }, + }, { name: "create SBM server with error", expectError: true, @@ -469,7 +492,7 @@ func TestAddSBMCmd(t *testing.T) { g.Expect(err).To(HaveOccurred()) } else { g.Expect(err).To(BeNil()) - g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + g.Expect(builder.GetOutput()).To(MatchJSON(tc.expectedOutput)) } }) } diff --git a/cmd/entities/hosts/reinstall.go b/cmd/entities/hosts/reinstall.go index 10a2ed0..d414c09 100644 --- a/cmd/entities/hosts/reinstall.go +++ b/cmd/entities/hosts/reinstall.go @@ -3,13 +3,16 @@ package hosts import ( "context" "fmt" - "log" - serverscom "github.com/serverscom/serverscom-go-client/pkg" "github.com/serverscom/srvctl/cmd/base" "github.com/spf13/cobra" ) +type AddedFlags struct { + Skeleton bool + InputPath string +} + type HostReinstaller interface { Reinstall(ctx context.Context, client *serverscom.Client, id string, input any) (any, error) NewReinstallInput() any @@ -44,13 +47,14 @@ func (c *SBMReinstallMgr) NewReinstallInput() any { } func newReinstallCmd(cmdContext *base.CmdContext, hostType *HostTypeCmd) *cobra.Command { - var path string + flags := &AddedFlags{} cmd := &cobra.Command{ Use: "reinstall ", Short: fmt.Sprintf("Reinstall OS for a %s", hostType.entityName), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { + formatter := cmdContext.GetOrCreateFormatter(cmd) manager := cmdContext.GetManager() ctx, cancel := base.SetupContext(cmd, manager) defer cancel() @@ -58,8 +62,17 @@ func newReinstallCmd(cmdContext *base.CmdContext, hostType *HostTypeCmd) *cobra. input := hostType.managers.reinstallMgr.NewReinstallInput() - if err := base.ReadInputJSON(path, cmd.InOrStdin(), input); err != nil { - return err + if flags.InputPath != "" { + if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), &input); err != nil { + return err + } + } else if flags.Skeleton { + return formatter.FormatSkeleton("hosts/reinstall.json") + } else { + required := []string{"input"} + if err := base.ValidateFlags(cmd, required); err != nil { + return err + } } scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() @@ -71,17 +84,14 @@ func newReinstallCmd(cmdContext *base.CmdContext, hostType *HostTypeCmd) *cobra. } if server != nil { - formatter := cmdContext.GetOrCreateFormatter(cmd) return formatter.Format(server) } return nil }, } - cmd.Flags().StringVarP(&path, "input", "i", "", "path to input file or '-' to read from stdin") - if err := cmd.MarkFlagRequired("input"); err != nil { - log.Fatal(err) - } + cmd.Flags().StringVarP(&flags.InputPath, "input", "i", "", "path to input file or '-' to read from stdin") + cmd.Flags().BoolVarP(&flags.Skeleton, "skeleton", "s", false, "JSON object with structure that is required to be passed") return cmd } diff --git a/cmd/entities/ssh-keys/add.go b/cmd/entities/ssh-keys/add.go index fd634ee..21b3004 100644 --- a/cmd/entities/ssh-keys/add.go +++ b/cmd/entities/ssh-keys/add.go @@ -65,8 +65,9 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { }, } - cmd.Flags().BoolVarP(&flags.Skeleton, "skeleton", "s", false, "JSON object with structure that is required to be passed") cmd.Flags().StringVarP(&flags.InputPath, "input", "i", "", "path to input file or '-' to read from stdin") + cmd.Flags().BoolVarP(&flags.Skeleton, "skeleton", "s", false, "JSON object with structure that is required to be passed") + cmd.Flags().StringVarP(&flags.Name, "name", "n", "", "A name of a SSH key") cmd.Flags().StringVarP(&flags.PublicKey, "public-key", "", "", "A public-key of a SSH key") cmd.Flags().StringArrayVarP(&flags.Labels, "label", "l", []string{}, "string in key=value format") diff --git a/cmd/entities/ssh-keys/ssh_keys_test.go b/cmd/entities/ssh-keys/ssh_keys_test.go index b228eed..dfe72d2 100644 --- a/cmd/entities/ssh-keys/ssh_keys_test.go +++ b/cmd/entities/ssh-keys/ssh_keys_test.go @@ -126,7 +126,7 @@ func TestAddSSHKeysCmd(t *testing.T) { g.Expect(err).To(HaveOccurred()) } else { g.Expect(err).To(BeNil()) - g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + g.Expect(builder.GetOutput()).To(MatchJSON(tc.expectedOutput)) } }) } diff --git a/internal/output/skeletons/templates/hosts/add_ds.json b/internal/output/skeletons/templates/hosts/add_ds.json new file mode 100644 index 0000000..7ac5a39 --- /dev/null +++ b/internal/output/skeletons/templates/hosts/add_ds.json @@ -0,0 +1,50 @@ +{ + "server_model_id": "", + "location_id": "", + "ram_size": "", + "uplink_models": { + "public": { + "id": "", + "bandwidth_model_id": "" + }, + "private": { + "id": "" + } + }, + "drives": { + "slots": [ + { + "position": "", + "drive_model_id": "" + }, + { + "position": "", + "drive_model_id": "" + } + ], + "layout": [ + { + "slot_positions": [], + "raid": "", + "partitions": [ + { + "target": "", + "size": "", + "fill": false, + "fs": "" + } + ] + } + ] + }, + "hosts": [ + { + "hostname": "", + "public_ipv4_network_id": "", + "private_ipv4_network_id": "", + "labels": { + "key": "value" + } + } + ] +} \ No newline at end of file diff --git a/internal/output/skeletons/templates/hosts/add_sbm.json b/internal/output/skeletons/templates/hosts/add_sbm.json new file mode 100644 index 0000000..cad2801 --- /dev/null +++ b/internal/output/skeletons/templates/hosts/add_sbm.json @@ -0,0 +1,14 @@ +{ + "sbm_flavor_model_id": "", + "location_id": "", + "hosts": [ + { + "hostname": "", + "public_ipv4_network_id": "", + "private_ipv4_network_id": "", + "labels": { + "key": "value" + } + } + ] +} \ No newline at end of file diff --git a/internal/output/skeletons/templates/hosts/reinstall.json b/internal/output/skeletons/templates/hosts/reinstall.json new file mode 100644 index 0000000..e69de29 diff --git a/internal/output/skeletons/templates/ssh-keys/add.json b/internal/output/skeletons/templates/ssh-keys/add.json index cadb3c8..583c2ca 100644 --- a/internal/output/skeletons/templates/ssh-keys/add.json +++ b/internal/output/skeletons/templates/ssh-keys/add.json @@ -1,4 +1,7 @@ { "name": "", - "public_key": "" + "public_key": "", + "labels": { + "key": "value" + } } \ No newline at end of file From c8ff351457e1d53fea8ffc2e165321272a6334af Mon Sep 17 00:00:00 2001 From: Anton Bozhiy Date: Fri, 23 Jan 2026 17:50:23 +0200 Subject: [PATCH 08/17] add skeleton flag to ssl custom add cmd --- cmd/entities/ssh-keys/add.go | 4 +-- cmd/entities/ssl/add.go | 7 ++++- cmd/entities/ssl/ssl_test.go | 28 +++++++++++++------ .../output/skeletons/templates/ssl/add.json | 8 ++++++ 4 files changed, 35 insertions(+), 12 deletions(-) create mode 100644 internal/output/skeletons/templates/ssl/add.json diff --git a/cmd/entities/ssh-keys/add.go b/cmd/entities/ssh-keys/add.go index 21b3004..0d2d860 100644 --- a/cmd/entities/ssh-keys/add.go +++ b/cmd/entities/ssh-keys/add.go @@ -1,7 +1,6 @@ package sshkeys import ( - "fmt" serverscom "github.com/serverscom/serverscom-go-client/pkg" "github.com/serverscom/srvctl/cmd/base" "github.com/spf13/cobra" @@ -39,8 +38,7 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { return err } } else if flags.Skeleton { - tmplPath := fmt.Sprintf("%s/%s.json", cmd.Parent().Name(), cmd.Name()) - return formatter.FormatSkeleton(tmplPath) + return formatter.FormatSkeleton("ssh-keys/add.json") } else { required := []string{"name", "public-key"} if err := base.ValidateFlags(cmd, required); err != nil { diff --git a/cmd/entities/ssl/add.go b/cmd/entities/ssl/add.go index c49b0de..908a1ea 100644 --- a/cmd/entities/ssl/add.go +++ b/cmd/entities/ssl/add.go @@ -9,6 +9,7 @@ import ( ) type AddedFlags struct { + Skeleton bool InputPath string Name string PublicKey string @@ -44,6 +45,7 @@ func newAddCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *cobra.Command Short: fmt.Sprintf("Create a %s", sslType.entityName), Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { + formatter := cmdContext.GetOrCreateFormatter(cmd) manager := cmdContext.GetManager() ctx, cancel := base.SetupContext(cmd, manager) defer cancel() @@ -56,6 +58,8 @@ func newAddCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *cobra.Command if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), input); err != nil { return err } + } else if flags.Skeleton { + return formatter.FormatSkeleton("ssl/add.json") } else { required := []string{"name", "public-key", "private-key"} if err := base.ValidateFlags(cmd, required); err != nil { @@ -75,7 +79,6 @@ func newAddCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *cobra.Command } if sslCert != nil { - formatter := cmdContext.GetOrCreateFormatter(cmd) return formatter.Format(sslCert) } @@ -84,6 +87,8 @@ func newAddCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *cobra.Command } cmd.Flags().StringVarP(&flags.InputPath, "input", "i", "", "path to input file or '-' to read from stdin") + cmd.Flags().BoolVarP(&flags.Skeleton, "skeleton", "s", false, "JSON object with structure that is required to be passed") + cmd.Flags().StringVarP(&flags.Name, "name", "n", "", "A name of a SSL certificate") cmd.Flags().StringVarP(&flags.PublicKey, "public-key", "", "", "A public-key of a SSL certificate") cmd.Flags().StringVarP(&flags.PrivateKey, "private-key", "", "", "A private-key of a SSL certificate") diff --git a/cmd/entities/ssl/ssl_test.go b/cmd/entities/ssl/ssl_test.go index d75cc92..fcfcb0b 100644 --- a/cmd/entities/ssl/ssl_test.go +++ b/cmd/entities/ssl/ssl_test.go @@ -15,13 +15,14 @@ import ( ) var ( - fixtureBasePath = filepath.Join("..", "..", "..", "testdata", "entities", "ssl") - fixedTime = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC) - expiresTime = fixedTime.AddDate(10, 0, 0) - testSha1Fingerpring = "21e84c9a3878673b377f0adf053290e8fc25cb80" - testIssuer = "servers.com" - testId = "testId" - testSSL = serverscom.SSLCertificate{ + fixtureBasePath = filepath.Join("..", "..", "..", "testdata", "entities", "ssl") + skeletonTemplatePath = filepath.Join("..", "..", "..", "internal", "output", "skeletons", "templates", "ssl") + fixedTime = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC) + expiresTime = fixedTime.AddDate(10, 0, 0) + testSha1Fingerpring = "21e84c9a3878673b377f0adf053290e8fc25cb80" + testIssuer = "servers.com" + testId = "testId" + testSSL = serverscom.SSLCertificate{ ID: testId, Name: "test-ssl-custom", Type: "custom", @@ -109,6 +110,17 @@ func TestAddCustomSSLCmd(t *testing.T) { Return(&testCustomSSL, nil) }, }, + { + name: "skeleton for custom ssl cert input", + output: "json", + args: []string{"--skeleton"}, + expectedOutput: testutils.ReadFixture(filepath.Join(skeletonTemplatePath, "add.json")), + configureMock: func(mock *mocks.MockSSLCertificatesService) { + mock.EXPECT(). + CreateCustom(gomock.Any(), gomock.Any()). + Times(0) + }, + }, { name: "with error", expectError: true, @@ -154,7 +166,7 @@ func TestAddCustomSSLCmd(t *testing.T) { g.Expect(err).To(HaveOccurred()) } else { g.Expect(err).To(BeNil()) - g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + g.Expect(builder.GetOutput()).To(MatchJSON(tc.expectedOutput)) } }) } diff --git a/internal/output/skeletons/templates/ssl/add.json b/internal/output/skeletons/templates/ssl/add.json new file mode 100644 index 0000000..7b12e30 --- /dev/null +++ b/internal/output/skeletons/templates/ssl/add.json @@ -0,0 +1,8 @@ +{ + "name": "", + "public_key": "", + "private_key": "", + "labels": { + "key": "value" + } +} \ No newline at end of file From e546355bf5c5d017ef1a7b188c2b7abf186fcdad Mon Sep 17 00:00:00 2001 From: Anton Bozhiy Date: Fri, 23 Jan 2026 17:55:55 +0200 Subject: [PATCH 09/17] add skeleton flag to l2-segments add cmd --- cmd/entities/l2_segments/add.go | 7 +++++- cmd/entities/l2_segments/l2_segments_test.go | 22 ++++++++++++++----- .../skeletons/templates/l2-segments/add.json | 14 ++++++++++++ 3 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 internal/output/skeletons/templates/l2-segments/add.json diff --git a/cmd/entities/l2_segments/add.go b/cmd/entities/l2_segments/add.go index a736775..dd08e50 100644 --- a/cmd/entities/l2_segments/add.go +++ b/cmd/entities/l2_segments/add.go @@ -9,6 +9,7 @@ import ( ) type AddedFlags struct { + Skeleton bool InputPath string Name string Type string @@ -26,6 +27,7 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { Long: "Add a new L2 segment", Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { + formatter := cmdContext.GetOrCreateFormatter(cmd) manager := cmdContext.GetManager() ctx, cancel := base.SetupContext(cmd, manager) @@ -39,6 +41,8 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), input); err != nil { return err } + } else if flags.Skeleton { + return formatter.FormatSkeleton("l2-segments/add.json") } else { required := []string{"type", "member"} if err := base.ValidateFlags(cmd, required); err != nil { @@ -57,7 +61,6 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { } if l2Segment != nil { - formatter := cmdContext.GetOrCreateFormatter(cmd) return formatter.Format(l2Segment) } return nil @@ -65,6 +68,8 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { } cmd.Flags().StringVarP(&flags.InputPath, "input", "i", "", "path to input file or '-' to read from stdin") + cmd.Flags().BoolVarP(&flags.Skeleton, "skeleton", "s", false, "JSON object with structure that is required to be passed") + cmd.Flags().StringVarP(&flags.Name, "name", "n", "", "A name of a L2 segment") cmd.Flags().StringVarP(&flags.Type, "type", "", "", "A type of a L2 segment") cmd.Flags().Int64VarP(&flags.LocationGroupID, "location-group-id", "", 0, "A private-key of a L2 segment") diff --git a/cmd/entities/l2_segments/l2_segments_test.go b/cmd/entities/l2_segments/l2_segments_test.go index 2307a05..9f09b51 100644 --- a/cmd/entities/l2_segments/l2_segments_test.go +++ b/cmd/entities/l2_segments/l2_segments_test.go @@ -15,10 +15,11 @@ import ( ) var ( - fixtureBasePath = filepath.Join("..", "..", "..", "testdata", "entities", "l2-segments") - testID = "testId" - testL2SegmentName = "testName" - fixedTime = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC) + fixtureBasePath = filepath.Join("..", "..", "..", "testdata", "entities", "l2-segments") + skeletonTemplatePath = filepath.Join("..", "..", "..", "internal", "output", "skeletons", "templates", "l2-segments") + testID = "testId" + testL2SegmentName = "testName" + fixedTime = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC) testL2Segment = serverscom.L2Segment{ ID: testID, @@ -337,6 +338,17 @@ func TestAddL2SegmentCmd(t *testing.T) { Return(&testL2Segment, nil) }, }, + { + name: "skeleton for l2 segment input", + output: "json", + args: []string{"--skeleton"}, + expectedOutput: testutils.ReadFixture(filepath.Join(skeletonTemplatePath, "add.json")), + configureMock: func(mock *mocks.MockL2SegmentsService) { + mock.EXPECT(). + Create(gomock.Any(), gomock.Any()). + Times(0) + }, + }, { name: "create l2 segment with error", expectError: true, @@ -378,7 +390,7 @@ func TestAddL2SegmentCmd(t *testing.T) { g.Expect(err).To(HaveOccurred()) } else { g.Expect(err).To(BeNil()) - g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + g.Expect(builder.GetOutput()).To(MatchJSON(tc.expectedOutput)) } }) } diff --git a/internal/output/skeletons/templates/l2-segments/add.json b/internal/output/skeletons/templates/l2-segments/add.json new file mode 100644 index 0000000..598a48c --- /dev/null +++ b/internal/output/skeletons/templates/l2-segments/add.json @@ -0,0 +1,14 @@ +{ + "name": "", + "type": "", + "location_group_id": "", + "members": [ + { + "id": "", + "mode": "" + } + ], + "labels": { + "key": "value" + } +} \ No newline at end of file From 16e5b6fbf1560d2b2e779a3fb5139a234c149911 Mon Sep 17 00:00:00 2001 From: Anton Bozhiy Date: Fri, 23 Jan 2026 18:14:09 +0200 Subject: [PATCH 10/17] add skeleton flag to l2-segments update/update_networks cmd --- cmd/entities/l2_segments/l2_segments_test.go | 26 ++++++++- cmd/entities/l2_segments/update.go | 55 +++++++++++++------ .../templates/l2-segments/update.json | 11 ++++ .../l2-segments/update_networks.json | 11 ++++ 4 files changed, 83 insertions(+), 20 deletions(-) create mode 100644 internal/output/skeletons/templates/l2-segments/update.json create mode 100644 internal/output/skeletons/templates/l2-segments/update_networks.json diff --git a/cmd/entities/l2_segments/l2_segments_test.go b/cmd/entities/l2_segments/l2_segments_test.go index 9f09b51..05de43a 100644 --- a/cmd/entities/l2_segments/l2_segments_test.go +++ b/cmd/entities/l2_segments/l2_segments_test.go @@ -432,6 +432,17 @@ func TestUpdateL2SegmentCmd(t *testing.T) { }, expectError: true, }, + { + name: "skeleton for update l2 segment input", + output: "json", + args: []string{"--skeleton"}, + expectedOutput: testutils.ReadFixture(filepath.Join(skeletonTemplatePath, "update.json")), + configureMock: func(mock *mocks.MockL2SegmentsService) { + mock.EXPECT(). + Update(gomock.Any(), gomock.Any(), gomock.Any()). + Times(0) + }, + }, } mockCtrl := gomock.NewController(t) @@ -468,7 +479,7 @@ func TestUpdateL2SegmentCmd(t *testing.T) { g.Expect(err).To(HaveOccurred()) } else { g.Expect(err).To(BeNil()) - g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + g.Expect(builder.GetOutput()).To(MatchJSON(tc.expectedOutput)) } }) } @@ -552,6 +563,17 @@ func TestUpdateL2NetworksCmd(t *testing.T) { Return(&testL2Segment, nil) }, }, + { + name: "skeleton for update l2 segment input", + output: "json", + args: []string{"--skeleton"}, + expectedOutput: testutils.ReadFixture(filepath.Join(skeletonTemplatePath, "update_networks.json")), + configureMock: func(mock *mocks.MockL2SegmentsService) { + mock.EXPECT(). + Update(gomock.Any(), gomock.Any(), gomock.Any()). + Times(0) + }, + }, { name: "update l2 segment networks with error", id: testID, @@ -600,7 +622,7 @@ func TestUpdateL2NetworksCmd(t *testing.T) { g.Expect(err).To(HaveOccurred()) } else { g.Expect(err).To(BeNil()) - g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + g.Expect(builder.GetOutput()).To(MatchJSON(tc.expectedOutput)) } }) } diff --git a/cmd/entities/l2_segments/update.go b/cmd/entities/l2_segments/update.go index 2be8c60..f7909df 100644 --- a/cmd/entities/l2_segments/update.go +++ b/cmd/entities/l2_segments/update.go @@ -1,15 +1,18 @@ package l2segments import ( - "log" - serverscom "github.com/serverscom/serverscom-go-client/pkg" "github.com/serverscom/srvctl/cmd/base" "github.com/spf13/cobra" ) +type UpdateFlags struct { + Skeleton bool + InputPath string +} + func newUpdateL2Cmd(cmdContext *base.CmdContext) *cobra.Command { - var path string + flags := &UpdateFlags{} cmd := &cobra.Command{ Use: "update ", @@ -17,6 +20,7 @@ func newUpdateL2Cmd(cmdContext *base.CmdContext) *cobra.Command { Long: "Update an L2 segment by id", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { + formatter := cmdContext.GetOrCreateFormatter(cmd) manager := cmdContext.GetManager() ctx, cancel := base.SetupContext(cmd, manager) @@ -25,8 +29,18 @@ func newUpdateL2Cmd(cmdContext *base.CmdContext) *cobra.Command { base.SetupProxy(cmd, manager) input := &serverscom.L2SegmentUpdateInput{} - if err := base.ReadInputJSON(path, cmd.InOrStdin(), input); err != nil { - return err + + if flags.InputPath != "" { + if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), input); err != nil { + return err + } + } else if flags.Skeleton { + return formatter.FormatSkeleton("l2-segments/update.json") + } else { + required := []string{"input"} + if err := base.ValidateFlags(cmd, required); err != nil { + return err + } } scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() @@ -38,23 +52,20 @@ func newUpdateL2Cmd(cmdContext *base.CmdContext) *cobra.Command { } if l2Segment != nil { - formatter := cmdContext.GetOrCreateFormatter(cmd) return formatter.Format(l2Segment) } return nil }, } - cmd.Flags().StringVarP(&path, "input", "i", "", "path to input file or '-' to read from stdin") - if err := cmd.MarkFlagRequired("input"); err != nil { - log.Fatal(err) - } + cmd.Flags().StringVarP(&flags.InputPath, "input", "i", "", "path to input file or '-' to read from stdin") + cmd.Flags().BoolVarP(&flags.Skeleton, "skeleton", "s", false, "JSON object with structure that is required to be passed") return cmd } func newUpdateL2NetworksCmd(cmdContext *base.CmdContext) *cobra.Command { - var path string + flags := &UpdateFlags{} cmd := &cobra.Command{ Use: "update-networks ", @@ -62,6 +73,7 @@ func newUpdateL2NetworksCmd(cmdContext *base.CmdContext) *cobra.Command { Long: "Update an L2 segment networks by id", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { + formatter := cmdContext.GetOrCreateFormatter(cmd) manager := cmdContext.GetManager() ctx, cancel := base.SetupContext(cmd, manager) @@ -70,8 +82,18 @@ func newUpdateL2NetworksCmd(cmdContext *base.CmdContext) *cobra.Command { base.SetupProxy(cmd, manager) input := &serverscom.L2SegmentChangeNetworksInput{} - if err := base.ReadInputJSON(path, cmd.InOrStdin(), input); err != nil { - return err + + if flags.InputPath != "" { + if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), input); err != nil { + return err + } + } else if flags.Skeleton { + return formatter.FormatSkeleton("l2-segments/update_networks.json") + } else { + required := []string{"input"} + if err := base.ValidateFlags(cmd, required); err != nil { + return err + } } scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() @@ -83,17 +105,14 @@ func newUpdateL2NetworksCmd(cmdContext *base.CmdContext) *cobra.Command { } if l2Segment != nil { - formatter := cmdContext.GetOrCreateFormatter(cmd) return formatter.Format(l2Segment) } return nil }, } - cmd.Flags().StringVarP(&path, "input", "i", "", "path to input file or '-' to read from stdin") - if err := cmd.MarkFlagRequired("input"); err != nil { - log.Fatal(err) - } + cmd.Flags().StringVarP(&flags.InputPath, "input", "i", "", "path to input file or '-' to read from stdin") + cmd.Flags().BoolVarP(&flags.Skeleton, "skeleton", "s", false, "JSON object with structure that is required to be passed") return cmd } diff --git a/internal/output/skeletons/templates/l2-segments/update.json b/internal/output/skeletons/templates/l2-segments/update.json new file mode 100644 index 0000000..fe55e5b --- /dev/null +++ b/internal/output/skeletons/templates/l2-segments/update.json @@ -0,0 +1,11 @@ +{ + "members": [ + { + "id": "", + "mode": "" + } + ], + "labels": { + "key": "value" + } +} \ No newline at end of file diff --git a/internal/output/skeletons/templates/l2-segments/update_networks.json b/internal/output/skeletons/templates/l2-segments/update_networks.json new file mode 100644 index 0000000..70c6a6f --- /dev/null +++ b/internal/output/skeletons/templates/l2-segments/update_networks.json @@ -0,0 +1,11 @@ +{ + "create": [ + { + "mask": "", + "distribution_method": "" + } + ], + "delete": [ + "id" + ] +} \ No newline at end of file From c21076e49dc765eb352099dc3dbfaa2fb4afaf21 Mon Sep 17 00:00:00 2001 From: Anton Bozhiy Date: Fri, 23 Jan 2026 18:26:50 +0200 Subject: [PATCH 11/17] add skeleton flag to lb add cmd --- cmd/entities/load_balancers/add.go | 31 ++++++++++------ .../load_balancers/load_balancers_test.go | 35 +++++++++++++++---- .../output/skeletons/templates/lb/add.json | 30 ++++++++++++++++ 3 files changed, 80 insertions(+), 16 deletions(-) create mode 100644 internal/output/skeletons/templates/lb/add.json diff --git a/cmd/entities/load_balancers/add.go b/cmd/entities/load_balancers/add.go index c7da11f..36149fd 100644 --- a/cmd/entities/load_balancers/add.go +++ b/cmd/entities/load_balancers/add.go @@ -3,13 +3,16 @@ package loadbalancers import ( "context" "fmt" - "log" - serverscom "github.com/serverscom/serverscom-go-client/pkg" "github.com/serverscom/srvctl/cmd/base" "github.com/spf13/cobra" ) +type AddFlags struct { + Skeleton bool + InputPath string +} + type LBCreator interface { Create(ctx context.Context, client *serverscom.Client, input any) (any, error) NewCreateInput() any @@ -44,12 +47,14 @@ func (c *LBL7CreateMgr) NewCreateInput() any { } func newAddCmd(cmdContext *base.CmdContext, lbType *LBTypeCmd) *cobra.Command { - var path string + flags := &AddFlags{} + cmd := &cobra.Command{ Use: "add --input ", Short: fmt.Sprintf("Create %s", lbType.entityName), Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { + formatter := cmdContext.GetOrCreateFormatter(cmd) manager := cmdContext.GetManager() ctx, cancel := base.SetupContext(cmd, manager) defer cancel() @@ -58,8 +63,17 @@ func newAddCmd(cmdContext *base.CmdContext, lbType *LBTypeCmd) *cobra.Command { input := lbType.managers.createMgr.NewCreateInput() - if err := base.ReadInputJSON(path, cmd.InOrStdin(), input); err != nil { - return err + if flags.InputPath != "" { + if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), input); err != nil { + return err + } + } else if flags.Skeleton { + return formatter.FormatSkeleton("lb/add.json") + } else { + required := []string{"input"} + if err := base.ValidateFlags(cmd, required); err != nil { + return err + } } scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() @@ -70,7 +84,6 @@ func newAddCmd(cmdContext *base.CmdContext, lbType *LBTypeCmd) *cobra.Command { } if lb != nil { - formatter := cmdContext.GetOrCreateFormatter(cmd) return formatter.Format(lb) } @@ -78,10 +91,8 @@ func newAddCmd(cmdContext *base.CmdContext, lbType *LBTypeCmd) *cobra.Command { }, } - cmd.Flags().StringVarP(&path, "input", "i", "", "path to input file or '-' to read from stdin") - if err := cmd.MarkFlagRequired("input"); err != nil { - log.Fatal(err) - } + cmd.Flags().StringVarP(&flags.InputPath, "input", "i", "", "path to input file or '-' to read from stdin") + cmd.Flags().BoolVarP(&flags.Skeleton, "skeleton", "s", false, "JSON object with structure that is required to be passed") return cmd } diff --git a/cmd/entities/load_balancers/load_balancers_test.go b/cmd/entities/load_balancers/load_balancers_test.go index 9c05252..03a85cc 100644 --- a/cmd/entities/load_balancers/load_balancers_test.go +++ b/cmd/entities/load_balancers/load_balancers_test.go @@ -14,10 +14,11 @@ import ( ) var ( - fixtureBasePath = filepath.Join("..", "..", "..", "testdata", "entities", "lb") - fixedTime = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC) - testId = "testId" - testLB = serverscom.LoadBalancer{ + fixtureBasePath = filepath.Join("..", "..", "..", "testdata", "entities", "lb") + skeletonTemplatePath = filepath.Join("..", "..", "..", "internal", "output", "skeletons", "templates", "lb") + fixedTime = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC) + testId = "testId" + testLB = serverscom.LoadBalancer{ ID: testId, Name: "test-l4-lb", Type: "l4", @@ -73,6 +74,17 @@ func TestAddL4LBCmd(t *testing.T) { Return(&testL4LB, nil) }, }, + { + name: "skeleton for l4 lb input", + output: "json", + args: []string{"--skeleton"}, + expectedOutput: testutils.ReadFixture(filepath.Join(skeletonTemplatePath, "add.json")), + configureMock: func(mock *mocks.MockLoadBalancersService) { + mock.EXPECT(). + CreateL4LoadBalancer(gomock.Any(), gomock.Any()). + Times(0) + }, + }, { name: "with error", expectError: true, @@ -118,7 +130,7 @@ func TestAddL4LBCmd(t *testing.T) { g.Expect(err).To(HaveOccurred()) } else { g.Expect(err).To(BeNil()) - g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + g.Expect(builder.GetOutput()).To(MatchJSON(tc.expectedOutput)) } }) } @@ -144,6 +156,17 @@ func TestAddL7LBCmd(t *testing.T) { Return(&testL7LB, nil) }, }, + { + name: "skeleton for l7 lb input", + output: "json", + args: []string{"--skeleton"}, + expectedOutput: testutils.ReadFixture(filepath.Join(skeletonTemplatePath, "add.json")), + configureMock: func(mock *mocks.MockLoadBalancersService) { + mock.EXPECT(). + CreateL7LoadBalancer(gomock.Any(), gomock.Any()). + Times(0) + }, + }, { name: "with error", expectError: true, @@ -189,7 +212,7 @@ func TestAddL7LBCmd(t *testing.T) { g.Expect(err).To(HaveOccurred()) } else { g.Expect(err).To(BeNil()) - g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + g.Expect(builder.GetOutput()).To(MatchJSON(tc.expectedOutput)) } }) } diff --git a/internal/output/skeletons/templates/lb/add.json b/internal/output/skeletons/templates/lb/add.json new file mode 100644 index 0000000..d194027 --- /dev/null +++ b/internal/output/skeletons/templates/lb/add.json @@ -0,0 +1,30 @@ +{ + "name": "", + "location_id": "", + "cluster_id": "", + "vhost_zones": [ + { + "id": "", + "ports": [], + "udp": true, + "proxy_protocol": true, + "upstream_id": "" + } + ], + "upstream_zones": [ + { + "id": "", + "udp": true, + "upstreams": [ + { + "ip": "", + "port": "", + "weight": "" + } + ] + } + ], + "labels": { + "key": "value" + } +} \ No newline at end of file From 945080c6b31f5b63eee7616b852a2c2c51fdde2f Mon Sep 17 00:00:00 2001 From: Anton Bozhiy Date: Fri, 23 Jan 2026 18:49:48 +0200 Subject: [PATCH 12/17] add skeleton flag to lb update cmd --- cmd/entities/load_balancers/add.go | 3 +- .../load_balancers/load_balancers_test.go | 30 ++++++++++++++--- cmd/entities/load_balancers/update.go | 32 +++++++++++++------ .../templates/lb/{add.json => add_l4.json} | 0 .../output/skeletons/templates/lb/add_l7.json | 30 +++++++++++++++++ .../skeletons/templates/lb/update_l4.json | 15 +++++++++ .../skeletons/templates/lb/update_l7.json | 20 ++++++++++++ 7 files changed, 115 insertions(+), 15 deletions(-) rename internal/output/skeletons/templates/lb/{add.json => add_l4.json} (100%) create mode 100644 internal/output/skeletons/templates/lb/add_l7.json create mode 100644 internal/output/skeletons/templates/lb/update_l4.json create mode 100644 internal/output/skeletons/templates/lb/update_l7.json diff --git a/cmd/entities/load_balancers/add.go b/cmd/entities/load_balancers/add.go index 36149fd..0352b02 100644 --- a/cmd/entities/load_balancers/add.go +++ b/cmd/entities/load_balancers/add.go @@ -68,7 +68,8 @@ func newAddCmd(cmdContext *base.CmdContext, lbType *LBTypeCmd) *cobra.Command { return err } } else if flags.Skeleton { - return formatter.FormatSkeleton("lb/add.json") + tmplPath := fmt.Sprintf("lb/add_%s.json", cmd.Parent().Name()) + return formatter.FormatSkeleton(tmplPath) } else { required := []string{"input"} if err := base.ValidateFlags(cmd, required); err != nil { diff --git a/cmd/entities/load_balancers/load_balancers_test.go b/cmd/entities/load_balancers/load_balancers_test.go index 03a85cc..acacbd3 100644 --- a/cmd/entities/load_balancers/load_balancers_test.go +++ b/cmd/entities/load_balancers/load_balancers_test.go @@ -78,7 +78,7 @@ func TestAddL4LBCmd(t *testing.T) { name: "skeleton for l4 lb input", output: "json", args: []string{"--skeleton"}, - expectedOutput: testutils.ReadFixture(filepath.Join(skeletonTemplatePath, "add.json")), + expectedOutput: testutils.ReadFixture(filepath.Join(skeletonTemplatePath, "add_l4.json")), configureMock: func(mock *mocks.MockLoadBalancersService) { mock.EXPECT(). CreateL4LoadBalancer(gomock.Any(), gomock.Any()). @@ -160,7 +160,7 @@ func TestAddL7LBCmd(t *testing.T) { name: "skeleton for l7 lb input", output: "json", args: []string{"--skeleton"}, - expectedOutput: testutils.ReadFixture(filepath.Join(skeletonTemplatePath, "add.json")), + expectedOutput: testutils.ReadFixture(filepath.Join(skeletonTemplatePath, "add_l7.json")), configureMock: func(mock *mocks.MockLoadBalancersService) { mock.EXPECT(). CreateL7LoadBalancer(gomock.Any(), gomock.Any()). @@ -550,6 +550,17 @@ func TestUpdateL4LBCmd(t *testing.T) { Return(&updatedL4LB, nil) }, }, + { + name: "skeleton for update l4 lb input", + output: "json", + args: []string{"--skeleton"}, + expectedOutput: testutils.ReadFixture(filepath.Join(skeletonTemplatePath, "update_l4.json")), + configureMock: func(mock *mocks.MockLoadBalancersService) { + mock.EXPECT(). + UpdateL4LoadBalancer(gomock.Any(), gomock.Any(), gomock.Any()). + Times(0) + }, + }, { name: "update l4 lb with error", id: testId, @@ -602,7 +613,7 @@ func TestUpdateL4LBCmd(t *testing.T) { g.Expect(err).To(HaveOccurred()) } else { g.Expect(err).To(BeNil()) - g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + g.Expect(builder.GetOutput()).To(MatchJSON(tc.expectedOutput)) } }) } @@ -635,6 +646,17 @@ func TestUpdateL7LBCmd(t *testing.T) { Return(&updatedL7LB, nil) }, }, + { + name: "skeleton for update l7 lb input", + output: "json", + args: []string{"--skeleton"}, + expectedOutput: testutils.ReadFixture(filepath.Join(skeletonTemplatePath, "update_l7.json")), + configureMock: func(mock *mocks.MockLoadBalancersService) { + mock.EXPECT(). + UpdateL7LoadBalancer(gomock.Any(), gomock.Any(), gomock.Any()). + Times(0) + }, + }, { name: "update l7 lb with error", id: testId, @@ -687,7 +709,7 @@ func TestUpdateL7LBCmd(t *testing.T) { g.Expect(err).To(HaveOccurred()) } else { g.Expect(err).To(BeNil()) - g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + g.Expect(builder.GetOutput()).To(MatchJSON(tc.expectedOutput)) } }) } diff --git a/cmd/entities/load_balancers/update.go b/cmd/entities/load_balancers/update.go index 689b8a3..88975a4 100644 --- a/cmd/entities/load_balancers/update.go +++ b/cmd/entities/load_balancers/update.go @@ -3,13 +3,16 @@ package loadbalancers import ( "context" "fmt" - "log" - serverscom "github.com/serverscom/serverscom-go-client/pkg" "github.com/serverscom/srvctl/cmd/base" "github.com/spf13/cobra" ) +type UpdateFlags struct { + Skeleton bool + InputPath string +} + type LBUpdater interface { Update(ctx context.Context, client *serverscom.Client, id string, input any) (any, error) NewUpdateInput() any @@ -44,12 +47,14 @@ func (c *LBL7UpdateMgr) NewUpdateInput() any { } func newUpdateCmd(cmdContext *base.CmdContext, lbType *LBTypeCmd) *cobra.Command { - var path string + flags := &UpdateFlags{} + cmd := &cobra.Command{ Use: "update --input ", Short: fmt.Sprintf("Update %s", lbType.entityName), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { + formatter := cmdContext.GetOrCreateFormatter(cmd) manager := cmdContext.GetManager() ctx, cancel := base.SetupContext(cmd, manager) defer cancel() @@ -58,8 +63,18 @@ func newUpdateCmd(cmdContext *base.CmdContext, lbType *LBTypeCmd) *cobra.Command input := lbType.managers.updateMgr.NewUpdateInput() - if err := base.ReadInputJSON(path, cmd.InOrStdin(), input); err != nil { - return err + if flags.InputPath != "" { + if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), input); err != nil { + return err + } + } else if flags.Skeleton { + tmplPath := fmt.Sprintf("lb/update_%s.json", cmd.Parent().Name()) + return formatter.FormatSkeleton(tmplPath) + } else { + required := []string{"input"} + if err := base.ValidateFlags(cmd, required); err != nil { + return err + } } scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() @@ -71,7 +86,6 @@ func newUpdateCmd(cmdContext *base.CmdContext, lbType *LBTypeCmd) *cobra.Command } if lb != nil { - formatter := cmdContext.GetOrCreateFormatter(cmd) return formatter.Format(lb) } @@ -79,10 +93,8 @@ func newUpdateCmd(cmdContext *base.CmdContext, lbType *LBTypeCmd) *cobra.Command }, } - cmd.Flags().StringVarP(&path, "input", "i", "", "path to input file or '-' to read from stdin") - if err := cmd.MarkFlagRequired("input"); err != nil { - log.Fatal(err) - } + cmd.Flags().StringVarP(&flags.InputPath, "input", "i", "", "path to input file or '-' to read from stdin") + cmd.Flags().BoolVarP(&flags.Skeleton, "skeleton", "s", false, "JSON object with structure that is required to be passed") return cmd } diff --git a/internal/output/skeletons/templates/lb/add.json b/internal/output/skeletons/templates/lb/add_l4.json similarity index 100% rename from internal/output/skeletons/templates/lb/add.json rename to internal/output/skeletons/templates/lb/add_l4.json diff --git a/internal/output/skeletons/templates/lb/add_l7.json b/internal/output/skeletons/templates/lb/add_l7.json new file mode 100644 index 0000000..d194027 --- /dev/null +++ b/internal/output/skeletons/templates/lb/add_l7.json @@ -0,0 +1,30 @@ +{ + "name": "", + "location_id": "", + "cluster_id": "", + "vhost_zones": [ + { + "id": "", + "ports": [], + "udp": true, + "proxy_protocol": true, + "upstream_id": "" + } + ], + "upstream_zones": [ + { + "id": "", + "udp": true, + "upstreams": [ + { + "ip": "", + "port": "", + "weight": "" + } + ] + } + ], + "labels": { + "key": "value" + } +} \ No newline at end of file diff --git a/internal/output/skeletons/templates/lb/update_l4.json b/internal/output/skeletons/templates/lb/update_l4.json new file mode 100644 index 0000000..31c2886 --- /dev/null +++ b/internal/output/skeletons/templates/lb/update_l4.json @@ -0,0 +1,15 @@ +{ + "id": "", + "name": "", + "type": "l4", + "status": "", + "external_addresses": [ + "127.0.0.1" + ], + "location_id": "", + "store_logs": false, + "cluster_id": "", + "labels": { + "key": "value" + } +} \ No newline at end of file diff --git a/internal/output/skeletons/templates/lb/update_l7.json b/internal/output/skeletons/templates/lb/update_l7.json new file mode 100644 index 0000000..49e9b3e --- /dev/null +++ b/internal/output/skeletons/templates/lb/update_l7.json @@ -0,0 +1,20 @@ +{ + "id": "", + "name": "", + "type": "l7", + "domains": [ + "test.com" + ], + "status": "", + "external_addresses": [ + "127.0.0.1" + ], + "location_id": "", + "geoip": false, + "store_logs": false, + "store_logs_region_id": "", + "cluster_id": "", + "labels": { + "key": "value" + } +} \ No newline at end of file From 8fae9a6dc5183fc907470698159d92e109bbe0ed Mon Sep 17 00:00:00 2001 From: Anton Bozhiy Date: Mon, 26 Jan 2026 20:29:29 +0200 Subject: [PATCH 13/17] fix formatter output for skeleton --- cmd/entities/hosts/add.go | 2 ++ cmd/entities/hosts/reinstall.go | 1 + cmd/entities/l2_segments/add.go | 1 + cmd/entities/l2_segments/update.go | 2 ++ cmd/entities/load_balancers/add.go | 1 + cmd/entities/load_balancers/update.go | 1 + cmd/entities/ssh-keys/add.go | 1 + cmd/entities/ssl/add.go | 1 + internal/output/formatter.go | 5 +++++ 9 files changed, 15 insertions(+) diff --git a/cmd/entities/hosts/add.go b/cmd/entities/hosts/add.go index 05009c9..2a5c6c6 100644 --- a/cmd/entities/hosts/add.go +++ b/cmd/entities/hosts/add.go @@ -150,6 +150,7 @@ func newAddDSCmd(cmdContext *base.CmdContext) *cobra.Command { return err } } else if flags.Skeleton { + formatter.SetOutput("json") return formatter.FormatSkeleton("hosts/add_ds.json") } else { required := []string{"input"} @@ -233,6 +234,7 @@ func newAddSBMCmd(cmdContext *base.CmdContext) *cobra.Command { return err } } else if flags.Skeleton { + formatter.SetOutput("json") return formatter.FormatSkeleton("hosts/add_sbm.json") } else { required := []string{"input"} diff --git a/cmd/entities/hosts/reinstall.go b/cmd/entities/hosts/reinstall.go index d414c09..1b06af3 100644 --- a/cmd/entities/hosts/reinstall.go +++ b/cmd/entities/hosts/reinstall.go @@ -67,6 +67,7 @@ func newReinstallCmd(cmdContext *base.CmdContext, hostType *HostTypeCmd) *cobra. return err } } else if flags.Skeleton { + formatter.SetOutput("json") return formatter.FormatSkeleton("hosts/reinstall.json") } else { required := []string{"input"} diff --git a/cmd/entities/l2_segments/add.go b/cmd/entities/l2_segments/add.go index dd08e50..644fedd 100644 --- a/cmd/entities/l2_segments/add.go +++ b/cmd/entities/l2_segments/add.go @@ -42,6 +42,7 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { return err } } else if flags.Skeleton { + formatter.SetOutput("json") return formatter.FormatSkeleton("l2-segments/add.json") } else { required := []string{"type", "member"} diff --git a/cmd/entities/l2_segments/update.go b/cmd/entities/l2_segments/update.go index f7909df..b2f6378 100644 --- a/cmd/entities/l2_segments/update.go +++ b/cmd/entities/l2_segments/update.go @@ -35,6 +35,7 @@ func newUpdateL2Cmd(cmdContext *base.CmdContext) *cobra.Command { return err } } else if flags.Skeleton { + formatter.SetOutput("json") return formatter.FormatSkeleton("l2-segments/update.json") } else { required := []string{"input"} @@ -88,6 +89,7 @@ func newUpdateL2NetworksCmd(cmdContext *base.CmdContext) *cobra.Command { return err } } else if flags.Skeleton { + formatter.SetOutput("json") return formatter.FormatSkeleton("l2-segments/update_networks.json") } else { required := []string{"input"} diff --git a/cmd/entities/load_balancers/add.go b/cmd/entities/load_balancers/add.go index 0352b02..948ec57 100644 --- a/cmd/entities/load_balancers/add.go +++ b/cmd/entities/load_balancers/add.go @@ -68,6 +68,7 @@ func newAddCmd(cmdContext *base.CmdContext, lbType *LBTypeCmd) *cobra.Command { return err } } else if flags.Skeleton { + formatter.SetOutput("json") tmplPath := fmt.Sprintf("lb/add_%s.json", cmd.Parent().Name()) return formatter.FormatSkeleton(tmplPath) } else { diff --git a/cmd/entities/load_balancers/update.go b/cmd/entities/load_balancers/update.go index 88975a4..7689b9a 100644 --- a/cmd/entities/load_balancers/update.go +++ b/cmd/entities/load_balancers/update.go @@ -68,6 +68,7 @@ func newUpdateCmd(cmdContext *base.CmdContext, lbType *LBTypeCmd) *cobra.Command return err } } else if flags.Skeleton { + formatter.SetOutput("json") tmplPath := fmt.Sprintf("lb/update_%s.json", cmd.Parent().Name()) return formatter.FormatSkeleton(tmplPath) } else { diff --git a/cmd/entities/ssh-keys/add.go b/cmd/entities/ssh-keys/add.go index 0d2d860..1595bd6 100644 --- a/cmd/entities/ssh-keys/add.go +++ b/cmd/entities/ssh-keys/add.go @@ -38,6 +38,7 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { return err } } else if flags.Skeleton { + formatter.SetOutput("json") return formatter.FormatSkeleton("ssh-keys/add.json") } else { required := []string{"name", "public-key"} diff --git a/cmd/entities/ssl/add.go b/cmd/entities/ssl/add.go index 908a1ea..45d1c72 100644 --- a/cmd/entities/ssl/add.go +++ b/cmd/entities/ssl/add.go @@ -59,6 +59,7 @@ func newAddCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *cobra.Command return err } } else if flags.Skeleton { + formatter.SetOutput("json") return formatter.FormatSkeleton("ssl/add.json") } else { required := []string{"name", "public-key", "private-key"} diff --git a/internal/output/formatter.go b/internal/output/formatter.go index 0b271c5..e600136 100644 --- a/internal/output/formatter.go +++ b/internal/output/formatter.go @@ -58,6 +58,11 @@ func (f *Formatter) SetTemplate(t *template.Template) { f.template = t } +// SetOutput sets output +func (f *Formatter) SetOutput(o string) { + f.output = o +} + // Format formats data according to format func (f *Formatter) Format(v any) error { switch f.output { From 7cf67802d3beef812418719d433f8e9c252f0f87 Mon Sep 17 00:00:00 2001 From: Anton Bozhiy Date: Tue, 27 Jan 2026 10:32:53 +0200 Subject: [PATCH 14/17] refactoring --- cmd/entities/hosts/add.go | 16 ++++++++++------ cmd/entities/hosts/reinstall.go | 8 +++++--- cmd/entities/l2_segments/add.go | 9 +++++---- cmd/entities/l2_segments/update.go | 18 ++++++++++-------- cmd/entities/load_balancers/add.go | 10 ++++++---- cmd/entities/load_balancers/update.go | 10 ++++++---- cmd/entities/ssh-keys/add.go | 9 +++++---- cmd/entities/ssl/add.go | 8 +++++--- internal/output/formatter.go | 2 ++ 9 files changed, 54 insertions(+), 36 deletions(-) diff --git a/cmd/entities/hosts/add.go b/cmd/entities/hosts/add.go index 2a5c6c6..7d97d8d 100644 --- a/cmd/entities/hosts/add.go +++ b/cmd/entities/hosts/add.go @@ -137,6 +137,11 @@ func newAddDSCmd(cmdContext *base.CmdContext) *cobra.Command { Args: cobra.ArbitraryArgs, RunE: func(cmd *cobra.Command, args []string) error { formatter := cmdContext.GetOrCreateFormatter(cmd) + + if flags.Skeleton { + return formatter.FormatSkeleton("hosts/add_ds.json") + } + manager := cmdContext.GetManager() ctx, cancel := base.SetupContext(cmd, manager) defer cancel() @@ -149,9 +154,6 @@ func newAddDSCmd(cmdContext *base.CmdContext) *cobra.Command { if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), &input); err != nil { return err } - } else if flags.Skeleton { - formatter.SetOutput("json") - return formatter.FormatSkeleton("hosts/add_ds.json") } else { required := []string{"input"} if err := base.ValidateFlags(cmd, required); err != nil { @@ -221,6 +223,11 @@ func newAddSBMCmd(cmdContext *base.CmdContext) *cobra.Command { Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { formatter := cmdContext.GetOrCreateFormatter(cmd) + + if flags.Skeleton { + return formatter.FormatSkeleton("hosts/add_sbm.json") + } + manager := cmdContext.GetManager() ctx, cancel := base.SetupContext(cmd, manager) defer cancel() @@ -233,9 +240,6 @@ func newAddSBMCmd(cmdContext *base.CmdContext) *cobra.Command { if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), &input); err != nil { return err } - } else if flags.Skeleton { - formatter.SetOutput("json") - return formatter.FormatSkeleton("hosts/add_sbm.json") } else { required := []string{"input"} if err := base.ValidateFlags(cmd, required); err != nil { diff --git a/cmd/entities/hosts/reinstall.go b/cmd/entities/hosts/reinstall.go index 1b06af3..657ff90 100644 --- a/cmd/entities/hosts/reinstall.go +++ b/cmd/entities/hosts/reinstall.go @@ -55,6 +55,11 @@ func newReinstallCmd(cmdContext *base.CmdContext, hostType *HostTypeCmd) *cobra. Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { formatter := cmdContext.GetOrCreateFormatter(cmd) + + if flags.Skeleton { + return formatter.FormatSkeleton("hosts/reinstall.json") + } + manager := cmdContext.GetManager() ctx, cancel := base.SetupContext(cmd, manager) defer cancel() @@ -66,9 +71,6 @@ func newReinstallCmd(cmdContext *base.CmdContext, hostType *HostTypeCmd) *cobra. if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), &input); err != nil { return err } - } else if flags.Skeleton { - formatter.SetOutput("json") - return formatter.FormatSkeleton("hosts/reinstall.json") } else { required := []string{"input"} if err := base.ValidateFlags(cmd, required); err != nil { diff --git a/cmd/entities/l2_segments/add.go b/cmd/entities/l2_segments/add.go index 644fedd..f297f20 100644 --- a/cmd/entities/l2_segments/add.go +++ b/cmd/entities/l2_segments/add.go @@ -28,8 +28,12 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { formatter := cmdContext.GetOrCreateFormatter(cmd) - manager := cmdContext.GetManager() + if flags.Skeleton { + return formatter.FormatSkeleton("l2-segments/add.json") + } + + manager := cmdContext.GetManager() ctx, cancel := base.SetupContext(cmd, manager) defer cancel() @@ -41,9 +45,6 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), input); err != nil { return err } - } else if flags.Skeleton { - formatter.SetOutput("json") - return formatter.FormatSkeleton("l2-segments/add.json") } else { required := []string{"type", "member"} if err := base.ValidateFlags(cmd, required); err != nil { diff --git a/cmd/entities/l2_segments/update.go b/cmd/entities/l2_segments/update.go index b2f6378..dea7901 100644 --- a/cmd/entities/l2_segments/update.go +++ b/cmd/entities/l2_segments/update.go @@ -21,8 +21,12 @@ func newUpdateL2Cmd(cmdContext *base.CmdContext) *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { formatter := cmdContext.GetOrCreateFormatter(cmd) - manager := cmdContext.GetManager() + if flags.Skeleton { + return formatter.FormatSkeleton("l2-segments/update.json") + } + + manager := cmdContext.GetManager() ctx, cancel := base.SetupContext(cmd, manager) defer cancel() @@ -34,9 +38,6 @@ func newUpdateL2Cmd(cmdContext *base.CmdContext) *cobra.Command { if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), input); err != nil { return err } - } else if flags.Skeleton { - formatter.SetOutput("json") - return formatter.FormatSkeleton("l2-segments/update.json") } else { required := []string{"input"} if err := base.ValidateFlags(cmd, required); err != nil { @@ -75,8 +76,12 @@ func newUpdateL2NetworksCmd(cmdContext *base.CmdContext) *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { formatter := cmdContext.GetOrCreateFormatter(cmd) - manager := cmdContext.GetManager() + if flags.Skeleton { + return formatter.FormatSkeleton("l2-segments/update_networks.json") + } + + manager := cmdContext.GetManager() ctx, cancel := base.SetupContext(cmd, manager) defer cancel() @@ -88,9 +93,6 @@ func newUpdateL2NetworksCmd(cmdContext *base.CmdContext) *cobra.Command { if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), input); err != nil { return err } - } else if flags.Skeleton { - formatter.SetOutput("json") - return formatter.FormatSkeleton("l2-segments/update_networks.json") } else { required := []string{"input"} if err := base.ValidateFlags(cmd, required); err != nil { diff --git a/cmd/entities/load_balancers/add.go b/cmd/entities/load_balancers/add.go index 948ec57..cceff1f 100644 --- a/cmd/entities/load_balancers/add.go +++ b/cmd/entities/load_balancers/add.go @@ -55,6 +55,12 @@ func newAddCmd(cmdContext *base.CmdContext, lbType *LBTypeCmd) *cobra.Command { Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { formatter := cmdContext.GetOrCreateFormatter(cmd) + + if flags.Skeleton { + tmplPath := fmt.Sprintf("lb/add_%s.json", cmd.Parent().Name()) + return formatter.FormatSkeleton(tmplPath) + } + manager := cmdContext.GetManager() ctx, cancel := base.SetupContext(cmd, manager) defer cancel() @@ -67,10 +73,6 @@ func newAddCmd(cmdContext *base.CmdContext, lbType *LBTypeCmd) *cobra.Command { if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), input); err != nil { return err } - } else if flags.Skeleton { - formatter.SetOutput("json") - tmplPath := fmt.Sprintf("lb/add_%s.json", cmd.Parent().Name()) - return formatter.FormatSkeleton(tmplPath) } else { required := []string{"input"} if err := base.ValidateFlags(cmd, required); err != nil { diff --git a/cmd/entities/load_balancers/update.go b/cmd/entities/load_balancers/update.go index 7689b9a..bfabf52 100644 --- a/cmd/entities/load_balancers/update.go +++ b/cmd/entities/load_balancers/update.go @@ -55,6 +55,12 @@ func newUpdateCmd(cmdContext *base.CmdContext, lbType *LBTypeCmd) *cobra.Command Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { formatter := cmdContext.GetOrCreateFormatter(cmd) + + if flags.Skeleton { + tmplPath := fmt.Sprintf("lb/update_%s.json", cmd.Parent().Name()) + return formatter.FormatSkeleton(tmplPath) + } + manager := cmdContext.GetManager() ctx, cancel := base.SetupContext(cmd, manager) defer cancel() @@ -67,10 +73,6 @@ func newUpdateCmd(cmdContext *base.CmdContext, lbType *LBTypeCmd) *cobra.Command if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), input); err != nil { return err } - } else if flags.Skeleton { - formatter.SetOutput("json") - tmplPath := fmt.Sprintf("lb/update_%s.json", cmd.Parent().Name()) - return formatter.FormatSkeleton(tmplPath) } else { required := []string{"input"} if err := base.ValidateFlags(cmd, required); err != nil { diff --git a/cmd/entities/ssh-keys/add.go b/cmd/entities/ssh-keys/add.go index 1595bd6..ebb57fe 100644 --- a/cmd/entities/ssh-keys/add.go +++ b/cmd/entities/ssh-keys/add.go @@ -23,9 +23,13 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { Long: "Add a new SSH key to account", Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { - manager := cmdContext.GetManager() formatter := cmdContext.GetOrCreateFormatter(cmd) + if flags.Skeleton { + return formatter.FormatSkeleton("ssh-keys/add.json") + } + + manager := cmdContext.GetManager() ctx, cancel := base.SetupContext(cmd, manager) defer cancel() @@ -37,9 +41,6 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), input); err != nil { return err } - } else if flags.Skeleton { - formatter.SetOutput("json") - return formatter.FormatSkeleton("ssh-keys/add.json") } else { required := []string{"name", "public-key"} if err := base.ValidateFlags(cmd, required); err != nil { diff --git a/cmd/entities/ssl/add.go b/cmd/entities/ssl/add.go index 45d1c72..b1814a1 100644 --- a/cmd/entities/ssl/add.go +++ b/cmd/entities/ssl/add.go @@ -46,6 +46,11 @@ func newAddCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *cobra.Command Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { formatter := cmdContext.GetOrCreateFormatter(cmd) + + if flags.Skeleton { + return formatter.FormatSkeleton("ssl/add.json") + } + manager := cmdContext.GetManager() ctx, cancel := base.SetupContext(cmd, manager) defer cancel() @@ -58,9 +63,6 @@ func newAddCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *cobra.Command if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), input); err != nil { return err } - } else if flags.Skeleton { - formatter.SetOutput("json") - return formatter.FormatSkeleton("ssl/add.json") } else { required := []string{"name", "public-key", "private-key"} if err := base.ValidateFlags(cmd, required); err != nil { diff --git a/internal/output/formatter.go b/internal/output/formatter.go index e600136..5fa1df9 100644 --- a/internal/output/formatter.go +++ b/internal/output/formatter.go @@ -92,6 +92,8 @@ func (f *Formatter) FormatText(v any) error { // FormatSkeleton formats skeleton template according to format func (f *Formatter) FormatSkeleton(v any) error { + f.SetOutput("json") + switch path := v.(type) { case string: raw, err := fs.ReadFile(skeletons.FS, path) From 5e9ec079e8d09ac8dc2fda382e03207d74dfa13d Mon Sep 17 00:00:00 2001 From: Anton Bozhiy Date: Tue, 27 Jan 2026 10:40:08 +0200 Subject: [PATCH 15/17] refactoring --- internal/output/formatter.go | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/internal/output/formatter.go b/internal/output/formatter.go index 5fa1df9..ba89409 100644 --- a/internal/output/formatter.go +++ b/internal/output/formatter.go @@ -90,24 +90,19 @@ func (f *Formatter) FormatText(v any) error { } } -// FormatSkeleton formats skeleton template according to format -func (f *Formatter) FormatSkeleton(v any) error { +// FormatSkeleton formats skeleton template in json format +func (f *Formatter) FormatSkeleton(path string) error { f.SetOutput("json") - switch path := v.(type) { - case string: - raw, err := fs.ReadFile(skeletons.FS, path) - if err != nil { - return err - } - - var data map[string]any - if err := json.Unmarshal(raw, &data); err != nil { - return err - } + raw, err := fs.ReadFile(skeletons.FS, path) + if err != nil { + return err + } - return f.Format(data) - default: - return f.Format(v) + var data map[string]any + if err := json.Unmarshal(raw, &data); err != nil { + return err } + + return f.Format(data) } From e42ea0eaec3847cfd8fc359cd29bb754abe89df5 Mon Sep 17 00:00:00 2001 From: Anton Bozhiy Date: Tue, 27 Jan 2026 12:12:45 +0200 Subject: [PATCH 16/17] add empty row to json output --- internal/output/formatter.go | 8 ++------ testdata/entities/account/get_balance.json | 2 +- testdata/entities/drive-models/get.json | 2 +- testdata/entities/drive-models/list.json | 2 +- testdata/entities/drive-models/list_all.json | 2 +- testdata/entities/hosts/create_ds_input.json | 2 +- testdata/entities/hosts/create_ds_resp.json | 2 +- testdata/entities/hosts/create_sbm_input.json | 2 +- testdata/entities/hosts/create_sbm_resp.json | 2 +- testdata/entities/hosts/features/list.json | 2 +- testdata/entities/hosts/features/list_all.json | 2 +- testdata/entities/hosts/get_ds.json | 2 +- testdata/entities/hosts/get_ds_network.json | 2 +- testdata/entities/hosts/get_kbm.json | 2 +- testdata/entities/hosts/get_sbm.json | 2 +- testdata/entities/hosts/list_drive_slots.json | 2 +- testdata/entities/hosts/list_drive_slots_all.json | 2 +- testdata/entities/hosts/list_ds.json | 2 +- testdata/entities/hosts/list_ds_all.json | 2 +- testdata/entities/hosts/list_ds_networks.json | 2 +- testdata/entities/hosts/list_ds_networks_all.json | 2 +- testdata/entities/hosts/list_hosts.json | 2 +- testdata/entities/hosts/list_kbm.json | 2 +- testdata/entities/hosts/list_kbm_all.json | 2 +- testdata/entities/hosts/list_kbm_networks.json | 2 +- testdata/entities/hosts/list_kbm_networks_all.json | 2 +- testdata/entities/hosts/list_power_feeds.json | 2 +- testdata/entities/hosts/list_sbm.json | 2 +- testdata/entities/hosts/list_sbm_all.json | 2 +- testdata/entities/hosts/oob/get.json | 2 +- testdata/entities/hosts/release_ds_resp.json | 2 +- testdata/entities/hosts/release_ds_scheduled_resp.json | 2 +- testdata/entities/hosts/services/list.json | 2 +- testdata/entities/hosts/services/list_all.json | 2 +- testdata/entities/hosts/set_kbm_power.json | 2 +- testdata/entities/hosts/update_ds_resp.json | 2 +- testdata/entities/hosts/update_kbm_resp.json | 2 +- testdata/entities/hosts/update_sbm_resp.json | 2 +- testdata/entities/invoices/get.json | 2 +- testdata/entities/invoices/list.json | 2 +- testdata/entities/invoices/list_all.json | 2 +- testdata/entities/k8s/get.json | 2 +- testdata/entities/k8s/get_node.json | 2 +- testdata/entities/k8s/list.json | 2 +- testdata/entities/k8s/list_all.json | 2 +- testdata/entities/k8s/list_all_nodes.json | 2 +- testdata/entities/k8s/list_nodes.json | 2 +- testdata/entities/k8s/update.json | 2 +- testdata/entities/l2-segments/create.json | 2 +- testdata/entities/l2-segments/get.json | 2 +- testdata/entities/l2-segments/list.json | 2 +- testdata/entities/l2-segments/list_all.json | 2 +- testdata/entities/l2-segments/list_groups.json | 2 +- testdata/entities/l2-segments/list_groups_all.json | 2 +- testdata/entities/l2-segments/list_members.json | 2 +- testdata/entities/l2-segments/list_members_all.json | 2 +- testdata/entities/l2-segments/list_networks.json | 2 +- testdata/entities/l2-segments/list_networks_all.json | 2 +- testdata/entities/l2-segments/update.json | 2 +- testdata/entities/l2-segments/update_input.json | 2 +- testdata/entities/l2-segments/update_networks_input.json | 2 +- testdata/entities/lb-clusters/get.json | 2 +- testdata/entities/lb-clusters/list.json | 2 +- testdata/entities/lb-clusters/list_all.json | 2 +- testdata/entities/lb/create_l4.json | 2 +- testdata/entities/lb/create_l7.json | 2 +- testdata/entities/lb/get_l4.json | 2 +- testdata/entities/lb/get_l7.json | 2 +- testdata/entities/lb/list_all.json | 2 +- testdata/entities/lb/list_l4.json | 2 +- testdata/entities/lb/list_l7.json | 2 +- testdata/entities/lb/update_input_l4.json | 2 +- testdata/entities/lb/update_input_l7.json | 2 +- testdata/entities/lb/update_l4.json | 2 +- testdata/entities/lb/update_l7.json | 2 +- testdata/entities/locations/get.json | 2 +- testdata/entities/locations/list.json | 2 +- testdata/entities/locations/list_all.json | 2 +- testdata/entities/network-pools/get.json | 2 +- testdata/entities/network-pools/get_subnet.json | 2 +- testdata/entities/network-pools/list.json | 2 +- testdata/entities/network-pools/list_all.json | 2 +- testdata/entities/network-pools/list_subnets.json | 2 +- testdata/entities/network-pools/list_subnets_all.json | 2 +- testdata/entities/network-pools/update.json | 2 +- testdata/entities/network-pools/update_subnet.json | 2 +- testdata/entities/os-options/get.json | 2 +- testdata/entities/os-options/list.json | 2 +- testdata/entities/os-options/list_all.json | 2 +- testdata/entities/ptr/get.json | 2 +- testdata/entities/ptr/list.json | 2 +- testdata/entities/ptr/list_all.json | 2 +- testdata/entities/racks/get.json | 2 +- testdata/entities/racks/list.json | 2 +- testdata/entities/racks/list_all.json | 2 +- testdata/entities/racks/update.json | 2 +- testdata/entities/sbm-models/get.json | 2 +- testdata/entities/sbm-models/list.json | 2 +- testdata/entities/sbm-models/list_all.json | 2 +- testdata/entities/server-models/get.json | 2 +- testdata/entities/server-models/list.json | 2 +- testdata/entities/server-models/list_all.json | 2 +- testdata/entities/server-ram-options/list.json | 2 +- testdata/entities/server-ram-options/list_all.json | 2 +- testdata/entities/ssh-keys/create.json | 2 +- testdata/entities/ssh-keys/get.json | 2 +- testdata/entities/ssh-keys/list.json | 2 +- testdata/entities/ssh-keys/list_all.json | 2 +- testdata/entities/ssh-keys/update.json | 2 +- testdata/entities/ssl/create_custom.json | 2 +- testdata/entities/ssl/get.json | 2 +- testdata/entities/ssl/get_custom.json | 2 +- testdata/entities/ssl/get_le.json | 2 +- testdata/entities/ssl/list_all.json | 2 +- testdata/entities/ssl/list_custom.json | 2 +- testdata/entities/ssl/list_le.json | 2 +- testdata/entities/ssl/update_custom.json | 2 +- testdata/entities/ssl/update_le.json | 2 +- testdata/entities/uplink-bandwidths/get.json | 2 +- testdata/entities/uplink-bandwidths/list.json | 2 +- testdata/entities/uplink-bandwidths/list_all.json | 2 +- testdata/entities/uplink-models/get.json | 2 +- testdata/entities/uplink-models/list.json | 2 +- testdata/entities/uplink-models/list_all.json | 2 +- 124 files changed, 125 insertions(+), 129 deletions(-) diff --git a/internal/output/formatter.go b/internal/output/formatter.go index ba89409..1bf5b22 100644 --- a/internal/output/formatter.go +++ b/internal/output/formatter.go @@ -71,6 +71,7 @@ func (f *Formatter) Format(v any) error { if err != nil { return err } + data = append(data, '\n') _, err = f.writer.Write(data) return err case "yaml": @@ -99,10 +100,5 @@ func (f *Formatter) FormatSkeleton(path string) error { return err } - var data map[string]any - if err := json.Unmarshal(raw, &data); err != nil { - return err - } - - return f.Format(data) + return f.Format(json.RawMessage(raw)) } diff --git a/testdata/entities/account/get_balance.json b/testdata/entities/account/get_balance.json index a8adf1e..f0d6f2c 100644 --- a/testdata/entities/account/get_balance.json +++ b/testdata/entities/account/get_balance.json @@ -2,4 +2,4 @@ "current_balance": 100, "next_invoice_total_due": 0, "currency": "EUR" -} \ No newline at end of file +} diff --git a/testdata/entities/drive-models/get.json b/testdata/entities/drive-models/get.json index 119c9bc..0cbcdb2 100644 --- a/testdata/entities/drive-models/get.json +++ b/testdata/entities/drive-models/get.json @@ -5,4 +5,4 @@ "interface": "SATA3", "form_factor": "2.5", "media_type": "SSD" -} \ No newline at end of file +} diff --git a/testdata/entities/drive-models/list.json b/testdata/entities/drive-models/list.json index 05fe66f..dd66a53 100644 --- a/testdata/entities/drive-models/list.json +++ b/testdata/entities/drive-models/list.json @@ -7,4 +7,4 @@ "form_factor": "2.5", "media_type": "SSD" } -] \ No newline at end of file +] diff --git a/testdata/entities/drive-models/list_all.json b/testdata/entities/drive-models/list_all.json index 8612f8d..8622ca2 100644 --- a/testdata/entities/drive-models/list_all.json +++ b/testdata/entities/drive-models/list_all.json @@ -15,4 +15,4 @@ "form_factor": "2.5", "media_type": "HDD" } -] \ No newline at end of file +] diff --git a/testdata/entities/hosts/create_ds_input.json b/testdata/entities/hosts/create_ds_input.json index 46c9750..6ccdd40 100644 --- a/testdata/entities/hosts/create_ds_input.json +++ b/testdata/entities/hosts/create_ds_input.json @@ -50,4 +50,4 @@ } } ] -} \ No newline at end of file +} diff --git a/testdata/entities/hosts/create_ds_resp.json b/testdata/entities/hosts/create_ds_resp.json index b85dc42..3fc68a6 100644 --- a/testdata/entities/hosts/create_ds_resp.json +++ b/testdata/entities/hosts/create_ds_resp.json @@ -32,4 +32,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/hosts/create_sbm_input.json b/testdata/entities/hosts/create_sbm_input.json index 4067671..acd0576 100644 --- a/testdata/entities/hosts/create_sbm_input.json +++ b/testdata/entities/hosts/create_sbm_input.json @@ -11,4 +11,4 @@ } } ] -} \ No newline at end of file +} diff --git a/testdata/entities/hosts/create_sbm_resp.json b/testdata/entities/hosts/create_sbm_resp.json index 274bbf0..528b035 100644 --- a/testdata/entities/hosts/create_sbm_resp.json +++ b/testdata/entities/hosts/create_sbm_resp.json @@ -32,4 +32,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/hosts/features/list.json b/testdata/entities/hosts/features/list.json index ee68a6f..a37bc13 100644 --- a/testdata/entities/hosts/features/list.json +++ b/testdata/entities/hosts/features/list.json @@ -3,4 +3,4 @@ "name": "disaggregated_public_ports", "status": "deactivated" } -] \ No newline at end of file +] diff --git a/testdata/entities/hosts/features/list_all.json b/testdata/entities/hosts/features/list_all.json index ab87de0..444aa83 100644 --- a/testdata/entities/hosts/features/list_all.json +++ b/testdata/entities/hosts/features/list_all.json @@ -7,4 +7,4 @@ "name": "no_public_network", "status": "unavailable" } -] \ No newline at end of file +] diff --git a/testdata/entities/hosts/get_ds.json b/testdata/entities/hosts/get_ds.json index ab3d509..d79f41f 100644 --- a/testdata/entities/hosts/get_ds.json +++ b/testdata/entities/hosts/get_ds.json @@ -30,4 +30,4 @@ "labels": null, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/hosts/get_ds_network.json b/testdata/entities/hosts/get_ds_network.json index 7e8137b..12661cc 100644 --- a/testdata/entities/hosts/get_ds_network.json +++ b/testdata/entities/hosts/get_ds_network.json @@ -10,4 +10,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z", "state": "" -} \ No newline at end of file +} diff --git a/testdata/entities/hosts/get_kbm.json b/testdata/entities/hosts/get_kbm.json index f24d9c8..078e8e5 100644 --- a/testdata/entities/hosts/get_kbm.json +++ b/testdata/entities/hosts/get_kbm.json @@ -33,4 +33,4 @@ "labels": null, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/hosts/get_sbm.json b/testdata/entities/hosts/get_sbm.json index 1acf992..bb21318 100644 --- a/testdata/entities/hosts/get_sbm.json +++ b/testdata/entities/hosts/get_sbm.json @@ -30,4 +30,4 @@ "labels": null, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/hosts/list_drive_slots.json b/testdata/entities/hosts/list_drive_slots.json index f200c38..fb163d3 100644 --- a/testdata/entities/hosts/list_drive_slots.json +++ b/testdata/entities/hosts/list_drive_slots.json @@ -12,4 +12,4 @@ "media_type": "SSD" } } -] \ No newline at end of file +] diff --git a/testdata/entities/hosts/list_drive_slots_all.json b/testdata/entities/hosts/list_drive_slots_all.json index 6f75e39..5364aae 100644 --- a/testdata/entities/hosts/list_drive_slots_all.json +++ b/testdata/entities/hosts/list_drive_slots_all.json @@ -25,4 +25,4 @@ "media_type": "SSD" } } -] \ No newline at end of file +] diff --git a/testdata/entities/hosts/list_ds.json b/testdata/entities/hosts/list_ds.json index b85dc42..3fc68a6 100644 --- a/testdata/entities/hosts/list_ds.json +++ b/testdata/entities/hosts/list_ds.json @@ -32,4 +32,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/hosts/list_ds_all.json b/testdata/entities/hosts/list_ds_all.json index 7e2d949..d25ef74 100644 --- a/testdata/entities/hosts/list_ds_all.json +++ b/testdata/entities/hosts/list_ds_all.json @@ -65,4 +65,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/hosts/list_ds_networks.json b/testdata/entities/hosts/list_ds_networks.json index b024e7f..669c5bf 100644 --- a/testdata/entities/hosts/list_ds_networks.json +++ b/testdata/entities/hosts/list_ds_networks.json @@ -12,4 +12,4 @@ "updated_at": "2025-01-01T12:00:00Z", "state": "" } -] \ No newline at end of file +] diff --git a/testdata/entities/hosts/list_ds_networks_all.json b/testdata/entities/hosts/list_ds_networks_all.json index 8dab390..a9aca37 100644 --- a/testdata/entities/hosts/list_ds_networks_all.json +++ b/testdata/entities/hosts/list_ds_networks_all.json @@ -25,4 +25,4 @@ "updated_at": "2025-01-01T12:00:00Z", "state": "" } -] \ No newline at end of file +] diff --git a/testdata/entities/hosts/list_hosts.json b/testdata/entities/hosts/list_hosts.json index 4f08206..f7004a0 100644 --- a/testdata/entities/hosts/list_hosts.json +++ b/testdata/entities/hosts/list_hosts.json @@ -31,4 +31,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/hosts/list_kbm.json b/testdata/entities/hosts/list_kbm.json index 22dee12..ebff703 100644 --- a/testdata/entities/hosts/list_kbm.json +++ b/testdata/entities/hosts/list_kbm.json @@ -35,4 +35,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/hosts/list_kbm_all.json b/testdata/entities/hosts/list_kbm_all.json index 381f282..c5d5564 100644 --- a/testdata/entities/hosts/list_kbm_all.json +++ b/testdata/entities/hosts/list_kbm_all.json @@ -71,4 +71,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/hosts/list_kbm_networks.json b/testdata/entities/hosts/list_kbm_networks.json index b024e7f..669c5bf 100644 --- a/testdata/entities/hosts/list_kbm_networks.json +++ b/testdata/entities/hosts/list_kbm_networks.json @@ -12,4 +12,4 @@ "updated_at": "2025-01-01T12:00:00Z", "state": "" } -] \ No newline at end of file +] diff --git a/testdata/entities/hosts/list_kbm_networks_all.json b/testdata/entities/hosts/list_kbm_networks_all.json index 8dab390..a9aca37 100644 --- a/testdata/entities/hosts/list_kbm_networks_all.json +++ b/testdata/entities/hosts/list_kbm_networks_all.json @@ -25,4 +25,4 @@ "updated_at": "2025-01-01T12:00:00Z", "state": "" } -] \ No newline at end of file +] diff --git a/testdata/entities/hosts/list_power_feeds.json b/testdata/entities/hosts/list_power_feeds.json index fff0c03..6fed4cc 100644 --- a/testdata/entities/hosts/list_power_feeds.json +++ b/testdata/entities/hosts/list_power_feeds.json @@ -9,4 +9,4 @@ "status": "off", "type": "physical" } -] \ No newline at end of file +] diff --git a/testdata/entities/hosts/list_sbm.json b/testdata/entities/hosts/list_sbm.json index 274bbf0..528b035 100644 --- a/testdata/entities/hosts/list_sbm.json +++ b/testdata/entities/hosts/list_sbm.json @@ -32,4 +32,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/hosts/list_sbm_all.json b/testdata/entities/hosts/list_sbm_all.json index 8abd21c..5338378 100644 --- a/testdata/entities/hosts/list_sbm_all.json +++ b/testdata/entities/hosts/list_sbm_all.json @@ -65,4 +65,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/hosts/oob/get.json b/testdata/entities/hosts/oob/get.json index 25e185b..c87233b 100644 --- a/testdata/entities/hosts/oob/get.json +++ b/testdata/entities/hosts/oob/get.json @@ -1,4 +1,4 @@ { "login": "test", "secret": "secret" -} \ No newline at end of file +} diff --git a/testdata/entities/hosts/release_ds_resp.json b/testdata/entities/hosts/release_ds_resp.json index ab3d509..d79f41f 100644 --- a/testdata/entities/hosts/release_ds_resp.json +++ b/testdata/entities/hosts/release_ds_resp.json @@ -30,4 +30,4 @@ "labels": null, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/hosts/release_ds_scheduled_resp.json b/testdata/entities/hosts/release_ds_scheduled_resp.json index c10f62b..30d3238 100644 --- a/testdata/entities/hosts/release_ds_scheduled_resp.json +++ b/testdata/entities/hosts/release_ds_scheduled_resp.json @@ -30,4 +30,4 @@ "labels": null, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/hosts/services/list.json b/testdata/entities/hosts/services/list.json index 9609b8d..7d1acf4 100644 --- a/testdata/entities/hosts/services/list.json +++ b/testdata/entities/hosts/services/list.json @@ -15,4 +15,4 @@ "subtotal": 100, "discount_rate": 5 } -] \ No newline at end of file +] diff --git a/testdata/entities/hosts/services/list_all.json b/testdata/entities/hosts/services/list_all.json index e15e374..c8713f5 100644 --- a/testdata/entities/hosts/services/list_all.json +++ b/testdata/entities/hosts/services/list_all.json @@ -31,4 +31,4 @@ "subtotal": 100, "discount_rate": 5 } -] \ No newline at end of file +] diff --git a/testdata/entities/hosts/set_kbm_power.json b/testdata/entities/hosts/set_kbm_power.json index 5010e3d..4a373bb 100644 --- a/testdata/entities/hosts/set_kbm_power.json +++ b/testdata/entities/hosts/set_kbm_power.json @@ -35,4 +35,4 @@ }, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/hosts/update_ds_resp.json b/testdata/entities/hosts/update_ds_resp.json index baa0191..271d8c3 100644 --- a/testdata/entities/hosts/update_ds_resp.json +++ b/testdata/entities/hosts/update_ds_resp.json @@ -32,4 +32,4 @@ }, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/hosts/update_kbm_resp.json b/testdata/entities/hosts/update_kbm_resp.json index 5010e3d..4a373bb 100644 --- a/testdata/entities/hosts/update_kbm_resp.json +++ b/testdata/entities/hosts/update_kbm_resp.json @@ -35,4 +35,4 @@ }, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/hosts/update_sbm_resp.json b/testdata/entities/hosts/update_sbm_resp.json index e63b086..475ae64 100644 --- a/testdata/entities/hosts/update_sbm_resp.json +++ b/testdata/entities/hosts/update_sbm_resp.json @@ -32,4 +32,4 @@ }, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/invoices/get.json b/testdata/entities/invoices/get.json index 755a9e5..586f699 100644 --- a/testdata/entities/invoices/get.json +++ b/testdata/entities/invoices/get.json @@ -9,4 +9,4 @@ "currency": "USD", "csv_url": "http://test.csv", "pdf_url": "http://test.pdf" -} \ No newline at end of file +} diff --git a/testdata/entities/invoices/list.json b/testdata/entities/invoices/list.json index 19d57ce..d44d4d0 100644 --- a/testdata/entities/invoices/list.json +++ b/testdata/entities/invoices/list.json @@ -9,4 +9,4 @@ "total_due": 1.23, "currency": "USD" } -] \ No newline at end of file +] diff --git a/testdata/entities/invoices/list_all.json b/testdata/entities/invoices/list_all.json index b663de9..5746e95 100644 --- a/testdata/entities/invoices/list_all.json +++ b/testdata/entities/invoices/list_all.json @@ -19,4 +19,4 @@ "total_due": 1.23, "currency": "USD" } -] \ No newline at end of file +] diff --git a/testdata/entities/k8s/get.json b/testdata/entities/k8s/get.json index ce8acdf..5c08bb4 100644 --- a/testdata/entities/k8s/get.json +++ b/testdata/entities/k8s/get.json @@ -6,4 +6,4 @@ "labels": null, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/k8s/get_node.json b/testdata/entities/k8s/get_node.json index 50948c6..251cf71 100644 --- a/testdata/entities/k8s/get_node.json +++ b/testdata/entities/k8s/get_node.json @@ -13,4 +13,4 @@ "labels": null, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/k8s/list.json b/testdata/entities/k8s/list.json index 571acab..a036d0b 100644 --- a/testdata/entities/k8s/list.json +++ b/testdata/entities/k8s/list.json @@ -8,4 +8,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/k8s/list_all.json b/testdata/entities/k8s/list_all.json index 84d3e3e..1e912ad 100644 --- a/testdata/entities/k8s/list_all.json +++ b/testdata/entities/k8s/list_all.json @@ -17,4 +17,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/k8s/list_all_nodes.json b/testdata/entities/k8s/list_all_nodes.json index 1bcc7b0..3830021 100644 --- a/testdata/entities/k8s/list_all_nodes.json +++ b/testdata/entities/k8s/list_all_nodes.json @@ -31,4 +31,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/k8s/list_nodes.json b/testdata/entities/k8s/list_nodes.json index 83339be..31ab3dd 100644 --- a/testdata/entities/k8s/list_nodes.json +++ b/testdata/entities/k8s/list_nodes.json @@ -15,4 +15,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/k8s/update.json b/testdata/entities/k8s/update.json index 9028f01..00e80f5 100644 --- a/testdata/entities/k8s/update.json +++ b/testdata/entities/k8s/update.json @@ -8,4 +8,4 @@ }, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/l2-segments/create.json b/testdata/entities/l2-segments/create.json index 4a070e2..623a19f 100644 --- a/testdata/entities/l2-segments/create.json +++ b/testdata/entities/l2-segments/create.json @@ -11,4 +11,4 @@ "labels": { "environment": "testing" } -} \ No newline at end of file +} diff --git a/testdata/entities/l2-segments/get.json b/testdata/entities/l2-segments/get.json index a050763..1b5153f 100644 --- a/testdata/entities/l2-segments/get.json +++ b/testdata/entities/l2-segments/get.json @@ -10,4 +10,4 @@ }, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/l2-segments/list.json b/testdata/entities/l2-segments/list.json index 1476bbf..2bc5983 100644 --- a/testdata/entities/l2-segments/list.json +++ b/testdata/entities/l2-segments/list.json @@ -12,4 +12,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/l2-segments/list_all.json b/testdata/entities/l2-segments/list_all.json index 73eb414..4e47c45 100644 --- a/testdata/entities/l2-segments/list_all.json +++ b/testdata/entities/l2-segments/list_all.json @@ -25,4 +25,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/l2-segments/list_groups.json b/testdata/entities/l2-segments/list_groups.json index 7411b6d..3a41d12 100644 --- a/testdata/entities/l2-segments/list_groups.json +++ b/testdata/entities/l2-segments/list_groups.json @@ -14,4 +14,4 @@ "Azure" ] } -] \ No newline at end of file +] diff --git a/testdata/entities/l2-segments/list_groups_all.json b/testdata/entities/l2-segments/list_groups_all.json index 141d539..0d72301 100644 --- a/testdata/entities/l2-segments/list_groups_all.json +++ b/testdata/entities/l2-segments/list_groups_all.json @@ -29,4 +29,4 @@ "Azure" ] } -] \ No newline at end of file +] diff --git a/testdata/entities/l2-segments/list_members.json b/testdata/entities/l2-segments/list_members.json index 5443b23..32768c4 100644 --- a/testdata/entities/l2-segments/list_members.json +++ b/testdata/entities/l2-segments/list_members.json @@ -11,4 +11,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/l2-segments/list_members_all.json b/testdata/entities/l2-segments/list_members_all.json index 59e194a..662220f 100644 --- a/testdata/entities/l2-segments/list_members_all.json +++ b/testdata/entities/l2-segments/list_members_all.json @@ -23,4 +23,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/l2-segments/list_networks.json b/testdata/entities/l2-segments/list_networks.json index 1c82fa5..4534467 100644 --- a/testdata/entities/l2-segments/list_networks.json +++ b/testdata/entities/l2-segments/list_networks.json @@ -12,4 +12,4 @@ "updated_at": "2025-01-01T12:00:00Z", "state": "" } -] \ No newline at end of file +] diff --git a/testdata/entities/l2-segments/list_networks_all.json b/testdata/entities/l2-segments/list_networks_all.json index ae7faaa..c1251c7 100644 --- a/testdata/entities/l2-segments/list_networks_all.json +++ b/testdata/entities/l2-segments/list_networks_all.json @@ -25,4 +25,4 @@ "updated_at": "2025-01-01T12:00:00Z", "state": "" } -] \ No newline at end of file +] diff --git a/testdata/entities/l2-segments/update.json b/testdata/entities/l2-segments/update.json index e280eea..d963920 100644 --- a/testdata/entities/l2-segments/update.json +++ b/testdata/entities/l2-segments/update.json @@ -10,4 +10,4 @@ }, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/l2-segments/update_input.json b/testdata/entities/l2-segments/update_input.json index 2c1449f..4b2ba19 100644 --- a/testdata/entities/l2-segments/update_input.json +++ b/testdata/entities/l2-segments/update_input.json @@ -8,4 +8,4 @@ "labels": { "new": "label" } -} \ No newline at end of file +} diff --git a/testdata/entities/l2-segments/update_networks_input.json b/testdata/entities/l2-segments/update_networks_input.json index 3f24b9e..bb7255c 100644 --- a/testdata/entities/l2-segments/update_networks_input.json +++ b/testdata/entities/l2-segments/update_networks_input.json @@ -8,4 +8,4 @@ "delete": [ "8mepW2dM" ] -} \ No newline at end of file +} diff --git a/testdata/entities/lb-clusters/get.json b/testdata/entities/lb-clusters/get.json index a42aa96..655867e 100644 --- a/testdata/entities/lb-clusters/get.json +++ b/testdata/entities/lb-clusters/get.json @@ -5,4 +5,4 @@ "status": "active", "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/lb-clusters/list.json b/testdata/entities/lb-clusters/list.json index 9185556..2c16f2a 100644 --- a/testdata/entities/lb-clusters/list.json +++ b/testdata/entities/lb-clusters/list.json @@ -7,4 +7,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/lb-clusters/list_all.json b/testdata/entities/lb-clusters/list_all.json index 46e6083..469c320 100644 --- a/testdata/entities/lb-clusters/list_all.json +++ b/testdata/entities/lb-clusters/list_all.json @@ -15,4 +15,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/lb/create_l4.json b/testdata/entities/lb/create_l4.json index 998d193..4871230 100644 --- a/testdata/entities/lb/create_l4.json +++ b/testdata/entities/lb/create_l4.json @@ -29,4 +29,4 @@ "labels": { "foo": "bar" } -} \ No newline at end of file +} diff --git a/testdata/entities/lb/create_l7.json b/testdata/entities/lb/create_l7.json index 83202fc..8e47229 100644 --- a/testdata/entities/lb/create_l7.json +++ b/testdata/entities/lb/create_l7.json @@ -29,4 +29,4 @@ "labels": { "foo": "bar" } -} \ No newline at end of file +} diff --git a/testdata/entities/lb/get_l4.json b/testdata/entities/lb/get_l4.json index 170ecfa..7a42954 100644 --- a/testdata/entities/lb/get_l4.json +++ b/testdata/entities/lb/get_l4.json @@ -14,4 +14,4 @@ }, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/lb/get_l7.json b/testdata/entities/lb/get_l7.json index ef2c0c7..4ed8d79 100644 --- a/testdata/entities/lb/get_l7.json +++ b/testdata/entities/lb/get_l7.json @@ -19,4 +19,4 @@ }, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/lb/list_all.json b/testdata/entities/lb/list_all.json index 3aa590d..921dc36 100644 --- a/testdata/entities/lb/list_all.json +++ b/testdata/entities/lb/list_all.json @@ -31,4 +31,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/lb/list_l4.json b/testdata/entities/lb/list_l4.json index 98feab8..8e2c32d 100644 --- a/testdata/entities/lb/list_l4.json +++ b/testdata/entities/lb/list_l4.json @@ -15,4 +15,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/lb/list_l7.json b/testdata/entities/lb/list_l7.json index 649cd84..fb64495 100644 --- a/testdata/entities/lb/list_l7.json +++ b/testdata/entities/lb/list_l7.json @@ -15,4 +15,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/lb/update_input_l4.json b/testdata/entities/lb/update_input_l4.json index a515d28..ca01ded 100644 --- a/testdata/entities/lb/update_input_l4.json +++ b/testdata/entities/lb/update_input_l4.json @@ -2,4 +2,4 @@ "labels": { "new": "label" } -} \ No newline at end of file +} diff --git a/testdata/entities/lb/update_input_l7.json b/testdata/entities/lb/update_input_l7.json index a515d28..ca01ded 100644 --- a/testdata/entities/lb/update_input_l7.json +++ b/testdata/entities/lb/update_input_l7.json @@ -2,4 +2,4 @@ "labels": { "new": "label" } -} \ No newline at end of file +} diff --git a/testdata/entities/lb/update_l4.json b/testdata/entities/lb/update_l4.json index dbca156..3b25260 100644 --- a/testdata/entities/lb/update_l4.json +++ b/testdata/entities/lb/update_l4.json @@ -14,4 +14,4 @@ }, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/lb/update_l7.json b/testdata/entities/lb/update_l7.json index 088d2ee..03a8f8a 100644 --- a/testdata/entities/lb/update_l7.json +++ b/testdata/entities/lb/update_l7.json @@ -19,4 +19,4 @@ }, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/locations/get.json b/testdata/entities/locations/get.json index 0c1c187..1e803f2 100644 --- a/testdata/entities/locations/get.json +++ b/testdata/entities/locations/get.json @@ -10,4 +10,4 @@ "l2_segments_enabled": false, "private_racks_enabled": false, "load_balancers_enabled": false -} \ No newline at end of file +} diff --git a/testdata/entities/locations/list.json b/testdata/entities/locations/list.json index b5cfeab..f3a5eff 100644 --- a/testdata/entities/locations/list.json +++ b/testdata/entities/locations/list.json @@ -12,4 +12,4 @@ "private_racks_enabled": false, "load_balancers_enabled": false } -] \ No newline at end of file +] diff --git a/testdata/entities/locations/list_all.json b/testdata/entities/locations/list_all.json index b54c022..e6a6483 100644 --- a/testdata/entities/locations/list_all.json +++ b/testdata/entities/locations/list_all.json @@ -25,4 +25,4 @@ "private_racks_enabled": false, "load_balancers_enabled": false } -] \ No newline at end of file +] diff --git a/testdata/entities/network-pools/get.json b/testdata/entities/network-pools/get.json index d148d10..78c58c5 100644 --- a/testdata/entities/network-pools/get.json +++ b/testdata/entities/network-pools/get.json @@ -12,4 +12,4 @@ }, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/network-pools/get_subnet.json b/testdata/entities/network-pools/get_subnet.json index e3d7974..1e3a0e6 100644 --- a/testdata/entities/network-pools/get_subnet.json +++ b/testdata/entities/network-pools/get_subnet.json @@ -7,4 +7,4 @@ "interface_type": "private", "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/network-pools/list.json b/testdata/entities/network-pools/list.json index f6b7576..0d6a8b8 100644 --- a/testdata/entities/network-pools/list.json +++ b/testdata/entities/network-pools/list.json @@ -14,4 +14,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/network-pools/list_all.json b/testdata/entities/network-pools/list_all.json index b212705..2e4bf74 100644 --- a/testdata/entities/network-pools/list_all.json +++ b/testdata/entities/network-pools/list_all.json @@ -29,4 +29,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/network-pools/list_subnets.json b/testdata/entities/network-pools/list_subnets.json index 22dab25..750b5de 100644 --- a/testdata/entities/network-pools/list_subnets.json +++ b/testdata/entities/network-pools/list_subnets.json @@ -9,4 +9,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/network-pools/list_subnets_all.json b/testdata/entities/network-pools/list_subnets_all.json index 62c04f8..170924a 100644 --- a/testdata/entities/network-pools/list_subnets_all.json +++ b/testdata/entities/network-pools/list_subnets_all.json @@ -19,4 +19,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/network-pools/update.json b/testdata/entities/network-pools/update.json index 2867a9e..0d3bbe9 100644 --- a/testdata/entities/network-pools/update.json +++ b/testdata/entities/network-pools/update.json @@ -12,4 +12,4 @@ }, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/network-pools/update_subnet.json b/testdata/entities/network-pools/update_subnet.json index 2a9dfc2..c71a13d 100644 --- a/testdata/entities/network-pools/update_subnet.json +++ b/testdata/entities/network-pools/update_subnet.json @@ -7,4 +7,4 @@ "interface_type": "private", "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/os-options/get.json b/testdata/entities/os-options/get.json index 6ec9858..80fb3ec 100644 --- a/testdata/entities/os-options/get.json +++ b/testdata/entities/os-options/get.json @@ -11,4 +11,4 @@ "xfs", "reiser" ] -} \ No newline at end of file +} diff --git a/testdata/entities/os-options/list.json b/testdata/entities/os-options/list.json index df659c6..4f5f0f3 100644 --- a/testdata/entities/os-options/list.json +++ b/testdata/entities/os-options/list.json @@ -13,4 +13,4 @@ "reiser" ] } -] \ No newline at end of file +] diff --git a/testdata/entities/os-options/list_all.json b/testdata/entities/os-options/list_all.json index 9388410..0a22ebe 100644 --- a/testdata/entities/os-options/list_all.json +++ b/testdata/entities/os-options/list_all.json @@ -27,4 +27,4 @@ "reiser" ] } -] \ No newline at end of file +] diff --git a/testdata/entities/ptr/get.json b/testdata/entities/ptr/get.json index 899243a..647841b 100644 --- a/testdata/entities/ptr/get.json +++ b/testdata/entities/ptr/get.json @@ -4,4 +4,4 @@ "domain": "ptr-test.example.com", "priority": 10, "ttl": 300 -} \ No newline at end of file +} diff --git a/testdata/entities/ptr/list.json b/testdata/entities/ptr/list.json index 01b67cd..0dd3d5d 100644 --- a/testdata/entities/ptr/list.json +++ b/testdata/entities/ptr/list.json @@ -6,4 +6,4 @@ "priority": 10, "ttl": 300 } -] \ No newline at end of file +] diff --git a/testdata/entities/ptr/list_all.json b/testdata/entities/ptr/list_all.json index a91e7da..8077e0c 100644 --- a/testdata/entities/ptr/list_all.json +++ b/testdata/entities/ptr/list_all.json @@ -13,4 +13,4 @@ "priority": 10, "ttl": 300 } -] \ No newline at end of file +] diff --git a/testdata/entities/racks/get.json b/testdata/entities/racks/get.json index 4a81494..d240715 100644 --- a/testdata/entities/racks/get.json +++ b/testdata/entities/racks/get.json @@ -6,4 +6,4 @@ "labels": { "foo": "bar" } -} \ No newline at end of file +} diff --git a/testdata/entities/racks/list.json b/testdata/entities/racks/list.json index a1a9498..099c279 100644 --- a/testdata/entities/racks/list.json +++ b/testdata/entities/racks/list.json @@ -8,4 +8,4 @@ "foo": "bar" } } -] \ No newline at end of file +] diff --git a/testdata/entities/racks/list_all.json b/testdata/entities/racks/list_all.json index 282c5e2..7077a85 100644 --- a/testdata/entities/racks/list_all.json +++ b/testdata/entities/racks/list_all.json @@ -17,4 +17,4 @@ "foo": "bar" } } -] \ No newline at end of file +] diff --git a/testdata/entities/racks/update.json b/testdata/entities/racks/update.json index 31324f5..9fc5588 100644 --- a/testdata/entities/racks/update.json +++ b/testdata/entities/racks/update.json @@ -6,4 +6,4 @@ "labels": { "new": "label" } -} \ No newline at end of file +} diff --git a/testdata/entities/sbm-models/get.json b/testdata/entities/sbm-models/get.json index 45b223c..f8f02d5 100644 --- a/testdata/entities/sbm-models/get.json +++ b/testdata/entities/sbm-models/get.json @@ -13,4 +13,4 @@ "private_uplink_model_name": "uplink-model-name-29", "bandwidth_id": 844, "bandwidth_name": "public-bandwidth-model-35" -} \ No newline at end of file +} diff --git a/testdata/entities/sbm-models/list.json b/testdata/entities/sbm-models/list.json index fc19dce..035a562 100644 --- a/testdata/entities/sbm-models/list.json +++ b/testdata/entities/sbm-models/list.json @@ -15,4 +15,4 @@ "bandwidth_id": 844, "bandwidth_name": "public-bandwidth-model-35" } -] \ No newline at end of file +] diff --git a/testdata/entities/sbm-models/list_all.json b/testdata/entities/sbm-models/list_all.json index 24f7af0..c2c524e 100644 --- a/testdata/entities/sbm-models/list_all.json +++ b/testdata/entities/sbm-models/list_all.json @@ -31,4 +31,4 @@ "bandwidth_id": 844, "bandwidth_name": "public-bandwidth-model-35" } -] \ No newline at end of file +] diff --git a/testdata/entities/server-models/get.json b/testdata/entities/server-models/get.json index c05f517..b34203a 100644 --- a/testdata/entities/server-models/get.json +++ b/testdata/entities/server-models/get.json @@ -27,4 +27,4 @@ "hot_swappable": true } ] -} \ No newline at end of file +} diff --git a/testdata/entities/server-models/list.json b/testdata/entities/server-models/list.json index 5e8df25..ce6deac 100644 --- a/testdata/entities/server-models/list.json +++ b/testdata/entities/server-models/list.json @@ -13,4 +13,4 @@ "raid_controller_name": "PERC H740P", "drive_slots_count": 16 } -] \ No newline at end of file +] diff --git a/testdata/entities/server-models/list_all.json b/testdata/entities/server-models/list_all.json index 2543f95..5c80543 100644 --- a/testdata/entities/server-models/list_all.json +++ b/testdata/entities/server-models/list_all.json @@ -27,4 +27,4 @@ "raid_controller_name": "PERC H740P", "drive_slots_count": 16 } -] \ No newline at end of file +] diff --git a/testdata/entities/server-ram-options/list.json b/testdata/entities/server-ram-options/list.json index a02d637..26ab2c6 100644 --- a/testdata/entities/server-ram-options/list.json +++ b/testdata/entities/server-ram-options/list.json @@ -3,4 +3,4 @@ "ram": 16384, "type": "DDR4" } -] \ No newline at end of file +] diff --git a/testdata/entities/server-ram-options/list_all.json b/testdata/entities/server-ram-options/list_all.json index 20690ae..56dc1e5 100644 --- a/testdata/entities/server-ram-options/list_all.json +++ b/testdata/entities/server-ram-options/list_all.json @@ -7,4 +7,4 @@ "ram": 32768, "type": "DDR4" } -] \ No newline at end of file +] diff --git a/testdata/entities/ssh-keys/create.json b/testdata/entities/ssh-keys/create.json index 25c2ffb..ffcf382 100644 --- a/testdata/entities/ssh-keys/create.json +++ b/testdata/entities/ssh-keys/create.json @@ -4,4 +4,4 @@ "labels": { "foo": "bar" } -} \ No newline at end of file +} diff --git a/testdata/entities/ssh-keys/get.json b/testdata/entities/ssh-keys/get.json index a776f79..0db7a01 100644 --- a/testdata/entities/ssh-keys/get.json +++ b/testdata/entities/ssh-keys/get.json @@ -6,4 +6,4 @@ }, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/ssh-keys/list.json b/testdata/entities/ssh-keys/list.json index 5f284fc..973c23a 100644 --- a/testdata/entities/ssh-keys/list.json +++ b/testdata/entities/ssh-keys/list.json @@ -8,4 +8,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/ssh-keys/list_all.json b/testdata/entities/ssh-keys/list_all.json index 140204b..c241c3d 100644 --- a/testdata/entities/ssh-keys/list_all.json +++ b/testdata/entities/ssh-keys/list_all.json @@ -17,4 +17,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/ssh-keys/update.json b/testdata/entities/ssh-keys/update.json index 6d47fba..29a73df 100644 --- a/testdata/entities/ssh-keys/update.json +++ b/testdata/entities/ssh-keys/update.json @@ -6,4 +6,4 @@ }, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/ssl/create_custom.json b/testdata/entities/ssl/create_custom.json index 3296721..8647ef6 100644 --- a/testdata/entities/ssl/create_custom.json +++ b/testdata/entities/ssl/create_custom.json @@ -5,4 +5,4 @@ "labels": { "foo": "bar" } -} \ No newline at end of file +} diff --git a/testdata/entities/ssl/get.json b/testdata/entities/ssl/get.json index be09683..e722288 100644 --- a/testdata/entities/ssl/get.json +++ b/testdata/entities/ssl/get.json @@ -14,4 +14,4 @@ "expires_at": "2035-01-01T12:00:00Z", "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/ssl/get_custom.json b/testdata/entities/ssl/get_custom.json index be09683..e722288 100644 --- a/testdata/entities/ssl/get_custom.json +++ b/testdata/entities/ssl/get_custom.json @@ -14,4 +14,4 @@ "expires_at": "2035-01-01T12:00:00Z", "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/ssl/get_le.json b/testdata/entities/ssl/get_le.json index 32aca80..e73798c 100644 --- a/testdata/entities/ssl/get_le.json +++ b/testdata/entities/ssl/get_le.json @@ -13,4 +13,4 @@ "expires_at": "2035-01-01T12:00:00Z", "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/ssl/list_all.json b/testdata/entities/ssl/list_all.json index 2c615ce..dfd0b77 100644 --- a/testdata/entities/ssl/list_all.json +++ b/testdata/entities/ssl/list_all.json @@ -33,4 +33,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/ssl/list_custom.json b/testdata/entities/ssl/list_custom.json index 9e24b5f..66b8abc 100644 --- a/testdata/entities/ssl/list_custom.json +++ b/testdata/entities/ssl/list_custom.json @@ -16,4 +16,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/ssl/list_le.json b/testdata/entities/ssl/list_le.json index 2f1256f..a6c3228 100644 --- a/testdata/entities/ssl/list_le.json +++ b/testdata/entities/ssl/list_le.json @@ -16,4 +16,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/ssl/update_custom.json b/testdata/entities/ssl/update_custom.json index f9a3fa1..2884657 100644 --- a/testdata/entities/ssl/update_custom.json +++ b/testdata/entities/ssl/update_custom.json @@ -14,4 +14,4 @@ "expires_at": "2035-01-01T12:00:00Z", "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/ssl/update_le.json b/testdata/entities/ssl/update_le.json index 0e9b6b6..3262390 100644 --- a/testdata/entities/ssl/update_le.json +++ b/testdata/entities/ssl/update_le.json @@ -13,4 +13,4 @@ "expires_at": "2035-01-01T12:00:00Z", "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/uplink-bandwidths/get.json b/testdata/entities/uplink-bandwidths/get.json index 11af735..b606667 100644 --- a/testdata/entities/uplink-bandwidths/get.json +++ b/testdata/entities/uplink-bandwidths/get.json @@ -3,4 +3,4 @@ "name": "20002 GB", "type": "bytes", "commit": 1000 -} \ No newline at end of file +} diff --git a/testdata/entities/uplink-bandwidths/list.json b/testdata/entities/uplink-bandwidths/list.json index 7e3453e..f860744 100644 --- a/testdata/entities/uplink-bandwidths/list.json +++ b/testdata/entities/uplink-bandwidths/list.json @@ -5,4 +5,4 @@ "type": "bytes", "commit": 1000 } -] \ No newline at end of file +] diff --git a/testdata/entities/uplink-bandwidths/list_all.json b/testdata/entities/uplink-bandwidths/list_all.json index a0f11ec..6fa46af 100644 --- a/testdata/entities/uplink-bandwidths/list_all.json +++ b/testdata/entities/uplink-bandwidths/list_all.json @@ -11,4 +11,4 @@ "type": "unmetered", "commit": 1000 } -] \ No newline at end of file +] diff --git a/testdata/entities/uplink-models/get.json b/testdata/entities/uplink-models/get.json index 02c78e6..6d2f110 100644 --- a/testdata/entities/uplink-models/get.json +++ b/testdata/entities/uplink-models/get.json @@ -4,4 +4,4 @@ "type": "public", "speed": 1000, "redundancy": true -} \ No newline at end of file +} diff --git a/testdata/entities/uplink-models/list.json b/testdata/entities/uplink-models/list.json index dedb9aa..1f8b775 100644 --- a/testdata/entities/uplink-models/list.json +++ b/testdata/entities/uplink-models/list.json @@ -6,4 +6,4 @@ "speed": 1000, "redundancy": true } -] \ No newline at end of file +] diff --git a/testdata/entities/uplink-models/list_all.json b/testdata/entities/uplink-models/list_all.json index 3014f94..81d610d 100644 --- a/testdata/entities/uplink-models/list_all.json +++ b/testdata/entities/uplink-models/list_all.json @@ -13,4 +13,4 @@ "speed": 1000, "redundancy": true } -] \ No newline at end of file +] From 588fc68df0c435680899cc929221a9b9e1d71224 Mon Sep 17 00:00:00 2001 From: Anton Bozhiy Date: Tue, 27 Jan 2026 16:16:20 +0200 Subject: [PATCH 17/17] add skeleton flag to cloud-instances --- cmd/entities/cloud-instances/add.go | 24 +++++++++++++++---- .../cloud-instances/cloud_instances_test.go | 22 +++++++++++++---- .../templates/cloud-instances/add.json | 9 +++++++ testdata/entities/cloud-backups/create.json | 2 +- testdata/entities/cloud-backups/get.json | 2 +- testdata/entities/cloud-backups/list.json | 2 +- testdata/entities/cloud-backups/list_all.json | 2 +- testdata/entities/cloud-backups/restore.json | 2 +- testdata/entities/cloud-backups/update.json | 2 +- testdata/entities/cloud-instances/create.json | 2 +- testdata/entities/cloud-instances/get.json | 2 +- testdata/entities/cloud-instances/list.json | 2 +- .../entities/cloud-instances/list_all.json | 2 +- .../entities/cloud-regions/add_snapshot.json | 2 +- .../cloud-regions/get_credentials.json | 2 +- testdata/entities/cloud-regions/list.json | 2 +- testdata/entities/cloud-regions/list_all.json | 2 +- .../entities/cloud-regions/list_flavors.json | 2 +- .../cloud-regions/list_flavors_all.json | 2 +- .../entities/cloud-regions/list_images.json | 2 +- .../cloud-regions/list_images_all.json | 2 +- .../cloud-regions/list_snapshots.json | 2 +- .../cloud-regions/list_snapshots_all.json | 2 +- testdata/entities/cloud-volumes/create.json | 2 +- testdata/entities/cloud-volumes/get.json | 2 +- testdata/entities/cloud-volumes/list.json | 2 +- testdata/entities/cloud-volumes/list_all.json | 2 +- testdata/entities/cloud-volumes/update.json | 2 +- 28 files changed, 70 insertions(+), 35 deletions(-) create mode 100644 internal/output/skeletons/templates/cloud-instances/add.json diff --git a/cmd/entities/cloud-instances/add.go b/cmd/entities/cloud-instances/add.go index ad4d98f..5e1d3da 100644 --- a/cmd/entities/cloud-instances/add.go +++ b/cmd/entities/cloud-instances/add.go @@ -7,6 +7,7 @@ import ( ) type AddedFlags struct { + Skeleton bool InputPath string } @@ -14,10 +15,16 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { flags := &AddedFlags{} cmd := &cobra.Command{ - Use: "add --input ", + Use: "add", Short: "Add cloud instance", Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { + formatter := cmdContext.GetOrCreateFormatter(cmd) + + if flags.Skeleton { + return formatter.FormatSkeleton("cloud-instances/add.json") + } + manager := cmdContext.GetManager() ctx, cancel := base.SetupContext(cmd, manager) defer cancel() @@ -25,8 +32,16 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { base.SetupProxy(cmd, manager) input := serverscom.CloudComputingInstanceCreateInput{} - if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), &input); err != nil { - return err + + if flags.InputPath != "" { + if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), &input); err != nil { + return err + } + } else { + required := []string{"input"} + if err := base.ValidateFlags(cmd, required); err != nil { + return err + } } scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() @@ -36,7 +51,6 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { } if out != nil { - formatter := cmdContext.GetOrCreateFormatter(cmd) return formatter.Format(out) } return nil @@ -44,7 +58,7 @@ func newAddCmd(cmdContext *base.CmdContext) *cobra.Command { } cmd.Flags().StringVarP(&flags.InputPath, "input", "i", "", "path to input file or '-' to read from stdin") - _ = cmd.MarkFlagRequired("input") + cmd.Flags().BoolVarP(&flags.Skeleton, "skeleton", "s", false, "JSON object with structure that is required to be passed") return cmd } diff --git a/cmd/entities/cloud-instances/cloud_instances_test.go b/cmd/entities/cloud-instances/cloud_instances_test.go index c137da2..3dbaa2d 100644 --- a/cmd/entities/cloud-instances/cloud_instances_test.go +++ b/cmd/entities/cloud-instances/cloud_instances_test.go @@ -14,10 +14,11 @@ import ( ) var ( - fixtureBasePath = filepath.Join("..", "..", "..", "testdata", "entities", "cloud-instances") - fixedTime = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC) - testCloudInstanceID = "test-instance-id" - testCloudInstance = serverscom.CloudComputingInstance{ + fixtureBasePath = filepath.Join("..", "..", "..", "testdata", "entities", "cloud-instances") + skeletonTemplatePath = filepath.Join("..", "..", "..", "internal", "output", "skeletons", "templates", "cloud-instances") + fixedTime = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC) + testCloudInstanceID = "test-instance-id" + testCloudInstance = serverscom.CloudComputingInstance{ ID: testCloudInstanceID, Name: "test-instance", RegionID: 1, @@ -308,6 +309,17 @@ func TestAddCloudInstancesCmd(t *testing.T) { Return(&testCloudInstance, nil) }, }, + { + name: "skeleton for cloud instance input", + output: "json", + args: []string{"--skeleton"}, + expectedOutput: testutils.ReadFixture(filepath.Join(skeletonTemplatePath, "add.json")), + configureMock: func(mock *mocks.MockCloudComputingInstancesService) { + mock.EXPECT(). + Create(gomock.Any(), gomock.Any()). + Times(0) + }, + }, { name: "create cloud instance with error", expectError: true, @@ -353,7 +365,7 @@ func TestAddCloudInstancesCmd(t *testing.T) { g.Expect(err).To(HaveOccurred()) } else { g.Expect(err).To(BeNil()) - g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + g.Expect(builder.GetOutput()).To(MatchJSON(tc.expectedOutput)) } }) } diff --git a/internal/output/skeletons/templates/cloud-instances/add.json b/internal/output/skeletons/templates/cloud-instances/add.json new file mode 100644 index 0000000..44d047a --- /dev/null +++ b/internal/output/skeletons/templates/cloud-instances/add.json @@ -0,0 +1,9 @@ +{ + "name": "", + "flavor_id": "", + "image_id": "", + "region_id": "", + "labels": { + "key": "value" + } +} \ No newline at end of file diff --git a/testdata/entities/cloud-backups/create.json b/testdata/entities/cloud-backups/create.json index e5b5369..438d7b2 100644 --- a/testdata/entities/cloud-backups/create.json +++ b/testdata/entities/cloud-backups/create.json @@ -6,4 +6,4 @@ "labels": { "env": "test" } -} \ No newline at end of file +} diff --git a/testdata/entities/cloud-backups/get.json b/testdata/entities/cloud-backups/get.json index 9f979bd..c5635e8 100644 --- a/testdata/entities/cloud-backups/get.json +++ b/testdata/entities/cloud-backups/get.json @@ -10,4 +10,4 @@ }, "created_at": "2025-01-01T12:00:00Z", "name": "test-backup" -} \ No newline at end of file +} diff --git a/testdata/entities/cloud-backups/list.json b/testdata/entities/cloud-backups/list.json index c78d7f4..0a00fa5 100644 --- a/testdata/entities/cloud-backups/list.json +++ b/testdata/entities/cloud-backups/list.json @@ -12,4 +12,4 @@ "created_at": "2025-01-01T12:00:00Z", "name": "test-backup" } -] \ No newline at end of file +] diff --git a/testdata/entities/cloud-backups/list_all.json b/testdata/entities/cloud-backups/list_all.json index e4f4b99..833780c 100644 --- a/testdata/entities/cloud-backups/list_all.json +++ b/testdata/entities/cloud-backups/list_all.json @@ -25,4 +25,4 @@ "created_at": "2025-01-02T12:00:00Z", "name": "test-backup-2" } -] \ No newline at end of file +] diff --git a/testdata/entities/cloud-backups/restore.json b/testdata/entities/cloud-backups/restore.json index c6b22d9..63ca149 100644 --- a/testdata/entities/cloud-backups/restore.json +++ b/testdata/entities/cloud-backups/restore.json @@ -10,4 +10,4 @@ }, "created_at": "2025-01-01T12:00:00Z", "name": "test-backup" -} \ No newline at end of file +} diff --git a/testdata/entities/cloud-backups/update.json b/testdata/entities/cloud-backups/update.json index f16dd1a..c05733e 100644 --- a/testdata/entities/cloud-backups/update.json +++ b/testdata/entities/cloud-backups/update.json @@ -10,4 +10,4 @@ }, "created_at": "2025-01-01T12:00:00Z", "name": "test-backup" -} \ No newline at end of file +} diff --git a/testdata/entities/cloud-instances/create.json b/testdata/entities/cloud-instances/create.json index 5ab8f64..7d00710 100644 --- a/testdata/entities/cloud-instances/create.json +++ b/testdata/entities/cloud-instances/create.json @@ -6,4 +6,4 @@ "labels": { "env": "test" } -} \ No newline at end of file +} diff --git a/testdata/entities/cloud-instances/get.json b/testdata/entities/cloud-instances/get.json index 4cd3249..f4b6c29 100644 --- a/testdata/entities/cloud-instances/get.json +++ b/testdata/entities/cloud-instances/get.json @@ -22,4 +22,4 @@ }, "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" -} \ No newline at end of file +} diff --git a/testdata/entities/cloud-instances/list.json b/testdata/entities/cloud-instances/list.json index c82de79..8c528e6 100644 --- a/testdata/entities/cloud-instances/list.json +++ b/testdata/entities/cloud-instances/list.json @@ -24,4 +24,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/cloud-instances/list_all.json b/testdata/entities/cloud-instances/list_all.json index b0beaf0..9551a69 100644 --- a/testdata/entities/cloud-instances/list_all.json +++ b/testdata/entities/cloud-instances/list_all.json @@ -49,4 +49,4 @@ "created_at": "2025-01-01T12:00:00Z", "updated_at": "2025-01-01T12:00:00Z" } -] \ No newline at end of file +] diff --git a/testdata/entities/cloud-regions/add_snapshot.json b/testdata/entities/cloud-regions/add_snapshot.json index 32b7dd6..23e114e 100644 --- a/testdata/entities/cloud-regions/add_snapshot.json +++ b/testdata/entities/cloud-regions/add_snapshot.json @@ -6,4 +6,4 @@ "status": "available", "is_backup": false, "file_url": "https://test.example.com/snap-123" -} \ No newline at end of file +} diff --git a/testdata/entities/cloud-regions/get_credentials.json b/testdata/entities/cloud-regions/get_credentials.json index b837f81..48ac9c3 100644 --- a/testdata/entities/cloud-regions/get_credentials.json +++ b/testdata/entities/cloud-regions/get_credentials.json @@ -3,4 +3,4 @@ "tenant_name": 12345, "url": "https://test.example.com", "username": 67890 -} \ No newline at end of file +} diff --git a/testdata/entities/cloud-regions/list.json b/testdata/entities/cloud-regions/list.json index f0742e0..568eb33 100644 --- a/testdata/entities/cloud-regions/list.json +++ b/testdata/entities/cloud-regions/list.json @@ -4,4 +4,4 @@ "name": "Test Region", "code": "TEST" } -] \ No newline at end of file +] diff --git a/testdata/entities/cloud-regions/list_all.json b/testdata/entities/cloud-regions/list_all.json index ca1435e..1cedb48 100644 --- a/testdata/entities/cloud-regions/list_all.json +++ b/testdata/entities/cloud-regions/list_all.json @@ -9,4 +9,4 @@ "name": "Test Region 2", "code": "TEST2" } -] \ No newline at end of file +] diff --git a/testdata/entities/cloud-regions/list_flavors.json b/testdata/entities/cloud-regions/list_flavors.json index 9de9f81..d86c005 100644 --- a/testdata/entities/cloud-regions/list_flavors.json +++ b/testdata/entities/cloud-regions/list_flavors.json @@ -3,4 +3,4 @@ "id": "flavor-123", "name": "Test Flavor" } -] \ No newline at end of file +] diff --git a/testdata/entities/cloud-regions/list_flavors_all.json b/testdata/entities/cloud-regions/list_flavors_all.json index 22cf1f7..f8cd084 100644 --- a/testdata/entities/cloud-regions/list_flavors_all.json +++ b/testdata/entities/cloud-regions/list_flavors_all.json @@ -7,4 +7,4 @@ "id": "flavor-456", "name": "Test Flavor 2" } -] \ No newline at end of file +] diff --git a/testdata/entities/cloud-regions/list_images.json b/testdata/entities/cloud-regions/list_images.json index 8111878..9228627 100644 --- a/testdata/entities/cloud-regions/list_images.json +++ b/testdata/entities/cloud-regions/list_images.json @@ -3,4 +3,4 @@ "id": "img-123", "name": "Test Image" } -] \ No newline at end of file +] diff --git a/testdata/entities/cloud-regions/list_images_all.json b/testdata/entities/cloud-regions/list_images_all.json index 8a6f983..9e5995f 100644 --- a/testdata/entities/cloud-regions/list_images_all.json +++ b/testdata/entities/cloud-regions/list_images_all.json @@ -7,4 +7,4 @@ "id": "img-456", "name": "Test Image 2" } -] \ No newline at end of file +] diff --git a/testdata/entities/cloud-regions/list_snapshots.json b/testdata/entities/cloud-regions/list_snapshots.json index e13d6f1..973f3d7 100644 --- a/testdata/entities/cloud-regions/list_snapshots.json +++ b/testdata/entities/cloud-regions/list_snapshots.json @@ -8,4 +8,4 @@ "is_backup": false, "file_url": "https://test.example.com/snap-123" } -] \ No newline at end of file +] diff --git a/testdata/entities/cloud-regions/list_snapshots_all.json b/testdata/entities/cloud-regions/list_snapshots_all.json index 80ad961..ab7fbda 100644 --- a/testdata/entities/cloud-regions/list_snapshots_all.json +++ b/testdata/entities/cloud-regions/list_snapshots_all.json @@ -17,4 +17,4 @@ "is_backup": false, "file_url": "https://test.example.com/snap-123" } -] \ No newline at end of file +] diff --git a/testdata/entities/cloud-volumes/create.json b/testdata/entities/cloud-volumes/create.json index 074bf1c..cb39366 100644 --- a/testdata/entities/cloud-volumes/create.json +++ b/testdata/entities/cloud-volumes/create.json @@ -6,4 +6,4 @@ "labels": { "foo": "bar" } -} \ No newline at end of file +} diff --git a/testdata/entities/cloud-volumes/get.json b/testdata/entities/cloud-volumes/get.json index 373a1ad..2fcc146 100644 --- a/testdata/entities/cloud-volumes/get.json +++ b/testdata/entities/cloud-volumes/get.json @@ -12,4 +12,4 @@ "description": "Test volume", "name": "test-volume", "attachments": null -} \ No newline at end of file +} diff --git a/testdata/entities/cloud-volumes/list.json b/testdata/entities/cloud-volumes/list.json index c1f7277..ba3694e 100644 --- a/testdata/entities/cloud-volumes/list.json +++ b/testdata/entities/cloud-volumes/list.json @@ -14,4 +14,4 @@ "name": "test-volume", "attachments": null } -] \ No newline at end of file +] diff --git a/testdata/entities/cloud-volumes/list_all.json b/testdata/entities/cloud-volumes/list_all.json index c1f7277..ba3694e 100644 --- a/testdata/entities/cloud-volumes/list_all.json +++ b/testdata/entities/cloud-volumes/list_all.json @@ -14,4 +14,4 @@ "name": "test-volume", "attachments": null } -] \ No newline at end of file +] diff --git a/testdata/entities/cloud-volumes/update.json b/testdata/entities/cloud-volumes/update.json index 869d84b..e56ee71 100644 --- a/testdata/entities/cloud-volumes/update.json +++ b/testdata/entities/cloud-volumes/update.json @@ -4,4 +4,4 @@ "labels": { "new": "label" } -} \ No newline at end of file +}