From 67120e53ddc7a2914cd8c9edf91f9743a73bb572 Mon Sep 17 00:00:00 2001 From: Anton Bozhiy Date: Wed, 14 Jan 2026 12:23:08 +0200 Subject: [PATCH 1/4] Expand ssl custom add command --- cmd/entities/ssl/add.go | 86 ++++++++++++++++++++++++++++++++---- cmd/entities/ssl/ssl_test.go | 25 ++++++++++- 2 files changed, 101 insertions(+), 10 deletions(-) diff --git a/cmd/entities/ssl/add.go b/cmd/entities/ssl/add.go index bc93930..4348af6 100644 --- a/cmd/entities/ssl/add.go +++ b/cmd/entities/ssl/add.go @@ -3,11 +3,19 @@ package ssl import ( "context" "fmt" - "log" - serverscom "github.com/serverscom/serverscom-go-client/pkg" "github.com/serverscom/srvctl/cmd/base" "github.com/spf13/cobra" + "strings" +) + +var ( + path string + name string + publicKey string + privateKey string + chainKey string + labels []string ) type SSLCreator interface { @@ -30,9 +38,8 @@ func (c *SSLCustomCreateMgr) NewCreateInput() any { } func newAddCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *cobra.Command { - var path string cmd := &cobra.Command{ - Use: "add --input ", + Use: "add", Short: fmt.Sprintf("Create a %s", sslType.entityName), Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { @@ -44,8 +51,17 @@ func newAddCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *cobra.Command input := sslType.managers.createMgr.NewCreateInput() - if err := base.ReadInputJSON(path, cmd.InOrStdin(), input); err != nil { - return err + if cmd.Flags().Changed("input") { + if err := base.ReadInputJSON(path, cmd.InOrStdin(), input); err != nil { + return err + } + } else { + if err := validateCustomSSLFlags(cmd); err != nil { + return err + } + if err := fillCustomSSLInput(cmd, input); err != nil { + return err + } } scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() @@ -65,9 +81,61 @@ func newAddCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *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(&name, "name", "n", "", "A name of a SSL certificate") + cmd.Flags().StringVarP(&publicKey, "public-key", "", "", "A public-key of a SSL certificate") + cmd.Flags().StringVarP(&privateKey, "private-key", "", "", "A private-key of a SSL certificate") + cmd.Flags().StringVarP(&chainKey, "chain-key", "", "", "A chain-key of a SSL certificate") + cmd.Flags().StringArrayVarP(&labels, "label", "l", []string{}, "string in key=value format") return cmd } + +func validateCustomSSLFlags(cmd *cobra.Command) error { + required := []string{"name", "public-key", "private-key", "chain-key"} + var missing []string + + for _, flag := range required { + if !cmd.Flags().Changed(flag) { + missing = append(missing, "--"+flag) + } + } + + if len(missing) > 0 { + return fmt.Errorf( + "use --input or provide all required flags (missing: %s)", + strings.Join(missing, ", "), + ) + } + + return nil +} + +func fillCustomSSLInput(cmd *cobra.Command, input any) error { + sslInput, ok := input.(*serverscom.SSLCertificateCreateCustomInput) + if !ok { + return fmt.Errorf("invalid input type for custom SSL") + } + + if cmd.Flags().Changed("name") { + sslInput.Name = name + } + if cmd.Flags().Changed("public-key") { + sslInput.PublicKey = publicKey + } + if cmd.Flags().Changed("private-key") { + sslInput.PrivateKey = privateKey + } + if cmd.Flags().Changed("chain-key") { + sslInput.ChainKey = chainKey + } + if cmd.Flags().Changed("label") { + labelsMap, err := base.ParseLabels(labels) + if err != nil { + return err + } + + sslInput.Labels = labelsMap + } + + return nil +} diff --git a/cmd/entities/ssl/ssl_test.go b/cmd/entities/ssl/ssl_test.go index cb5c5be..d75cc92 100644 --- a/cmd/entities/ssl/ssl_test.go +++ b/cmd/entities/ssl/ssl_test.go @@ -71,7 +71,7 @@ func TestAddCustomSSLCmd(t *testing.T) { expectError bool }{ { - name: "create custom ssl cert", + name: "create custom ssl cert with input", output: "json", expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_custom.json")), args: []string{"--input", filepath.Join(fixtureBasePath, "create_custom.json")}, @@ -86,6 +86,29 @@ func TestAddCustomSSLCmd(t *testing.T) { Return(&testCustomSSL, nil) }, }, + { + name: "create custom ssl cert", + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_custom.json")), + args: []string{ + "--name", "test-ssl-custom", + "--public-key", "-----TEST public-key-----", + "--private-key", "-----TEST private-key-----", + "--chain-key", "-----TEST chain-key-----", + "--label", "foo=bar", + }, + configureMock: func(mock *mocks.MockSSLCertificatesService) { + mock.EXPECT(). + CreateCustom(gomock.Any(), serverscom.SSLCertificateCreateCustomInput{ + Name: "test-ssl-custom", + PublicKey: "-----TEST public-key-----", + PrivateKey: "-----TEST private-key-----", + ChainKey: "-----TEST chain-key-----", + Labels: map[string]string{"foo": "bar"}, + }). + Return(&testCustomSSL, nil) + }, + }, { name: "with error", expectError: true, From 25d0e5cf52efa169eeb44a3d0ec4a2282739201c Mon Sep 17 00:00:00 2001 From: Anton Bozhiy Date: Wed, 14 Jan 2026 16:23:55 +0200 Subject: [PATCH 2/4] refactoring --- cmd/base/utils.go | 19 ++++++++++ cmd/entities/ssl/add.go | 77 ++++++++++++++++------------------------- 2 files changed, 49 insertions(+), 47 deletions(-) diff --git a/cmd/base/utils.go b/cmd/base/utils.go index e26a5c5..be8c7af 100644 --- a/cmd/base/utils.go +++ b/cmd/base/utils.go @@ -194,3 +194,22 @@ func fetchItems[T any](ctx context.Context, collection serverscom.Collection[T], return collection.List(ctx) } + +func ValidateFlags(cmd *cobra.Command, required []string) error { + var missing []string + + for _, flag := range required { + if !cmd.Flags().Changed(flag) { + missing = append(missing, "--"+flag) + } + } + + if len(missing) > 0 { + return fmt.Errorf( + "provide all required flags (missing: %s)", + strings.Join(missing, ", "), + ) + } + + return nil +} diff --git a/cmd/entities/ssl/add.go b/cmd/entities/ssl/add.go index 4348af6..ad88206 100644 --- a/cmd/entities/ssl/add.go +++ b/cmd/entities/ssl/add.go @@ -6,17 +6,16 @@ import ( serverscom "github.com/serverscom/serverscom-go-client/pkg" "github.com/serverscom/srvctl/cmd/base" "github.com/spf13/cobra" - "strings" ) -var ( - path string - name string - publicKey string - privateKey string - chainKey string - labels []string -) +type AddedFlags struct { + InputPath string + Name string + PublicKey string + PrivateKey string + ChainKey string + Labels []string +} type SSLCreator interface { Create(ctx context.Context, client *serverscom.Client, input any) (any, error) @@ -38,6 +37,8 @@ func (c *SSLCustomCreateMgr) NewCreateInput() any { } func newAddCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *cobra.Command { + flags := &AddedFlags{} + cmd := &cobra.Command{ Use: "add", Short: fmt.Sprintf("Create a %s", sslType.entityName), @@ -51,19 +52,21 @@ func newAddCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *cobra.Command input := sslType.managers.createMgr.NewCreateInput() - if cmd.Flags().Changed("input") { - if err := base.ReadInputJSON(path, cmd.InOrStdin(), input); err != nil { + if flags.InputPath != "" { + if err := base.ReadInputJSON(flags.InputPath, cmd.InOrStdin(), input); err != nil { return err } } else { - if err := validateCustomSSLFlags(cmd); err != nil { - return err - } - if err := fillCustomSSLInput(cmd, input); err != nil { + required := []string{"name", "public-key", "private-key", "chain-key"} + if err := base.ValidateFlags(cmd, required); err != nil { return err } } + if err := flags.FillInput(cmd, input); err != nil { + return err + } + scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() sslCert, err := sslType.managers.createMgr.Create(ctx, scClient, input) @@ -80,56 +83,36 @@ func newAddCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *cobra.Command }, } - cmd.Flags().StringVarP(&path, "input", "i", "", "path to input file or '-' to read from stdin") - cmd.Flags().StringVarP(&name, "name", "n", "", "A name of a SSL certificate") - cmd.Flags().StringVarP(&publicKey, "public-key", "", "", "A public-key of a SSL certificate") - cmd.Flags().StringVarP(&privateKey, "private-key", "", "", "A private-key of a SSL certificate") - cmd.Flags().StringVarP(&chainKey, "chain-key", "", "", "A chain-key of a SSL certificate") - cmd.Flags().StringArrayVarP(&labels, "label", "l", []string{}, "string in key=value format") + 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 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") + cmd.Flags().StringVarP(&flags.ChainKey, "chain-key", "", "", "A chain-key of a SSL certificate") + cmd.Flags().StringArrayVarP(&flags.Labels, "label", "l", []string{}, "string in key=value format") return cmd } -func validateCustomSSLFlags(cmd *cobra.Command) error { - required := []string{"name", "public-key", "private-key", "chain-key"} - var missing []string - - for _, flag := range required { - if !cmd.Flags().Changed(flag) { - missing = append(missing, "--"+flag) - } - } - - if len(missing) > 0 { - return fmt.Errorf( - "use --input or provide all required flags (missing: %s)", - strings.Join(missing, ", "), - ) - } - - return nil -} - -func fillCustomSSLInput(cmd *cobra.Command, input any) error { +func (f *AddedFlags) FillInput(cmd *cobra.Command, input any) error { sslInput, ok := input.(*serverscom.SSLCertificateCreateCustomInput) if !ok { return fmt.Errorf("invalid input type for custom SSL") } if cmd.Flags().Changed("name") { - sslInput.Name = name + sslInput.Name = f.Name } if cmd.Flags().Changed("public-key") { - sslInput.PublicKey = publicKey + sslInput.PublicKey = f.PublicKey } if cmd.Flags().Changed("private-key") { - sslInput.PrivateKey = privateKey + sslInput.PrivateKey = f.PrivateKey } if cmd.Flags().Changed("chain-key") { - sslInput.ChainKey = chainKey + sslInput.ChainKey = f.ChainKey } if cmd.Flags().Changed("label") { - labelsMap, err := base.ParseLabels(labels) + labelsMap, err := base.ParseLabels(f.Labels) if err != nil { return err } From af07047a689118fc040f41bb49f45788b454c443 Mon Sep 17 00:00:00 2001 From: Anton Bozhiy Date: Wed, 14 Jan 2026 18:21:51 +0200 Subject: [PATCH 3/4] fix required flags list --- cmd/entities/ssl/add.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/entities/ssl/add.go b/cmd/entities/ssl/add.go index ad88206..73eda1e 100644 --- a/cmd/entities/ssl/add.go +++ b/cmd/entities/ssl/add.go @@ -57,7 +57,7 @@ func newAddCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *cobra.Command return err } } else { - required := []string{"name", "public-key", "private-key", "chain-key"} + required := []string{"public-key", "private-key"} if err := base.ValidateFlags(cmd, required); err != nil { return err } From d62d339fe020b45c10d14efde547c87c376ad72d Mon Sep 17 00:00:00 2001 From: Anton Bozhiy Date: Wed, 14 Jan 2026 18:29:22 +0200 Subject: [PATCH 4/4] fix required flags list --- cmd/entities/ssl/add.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/entities/ssl/add.go b/cmd/entities/ssl/add.go index 73eda1e..c49b0de 100644 --- a/cmd/entities/ssl/add.go +++ b/cmd/entities/ssl/add.go @@ -57,7 +57,7 @@ func newAddCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *cobra.Command return err } } else { - required := []string{"public-key", "private-key"} + required := []string{"name", "public-key", "private-key"} if err := base.ValidateFlags(cmd, required); err != nil { return err }