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 bc93930..c49b0de 100644 --- a/cmd/entities/ssl/add.go +++ b/cmd/entities/ssl/add.go @@ -3,13 +3,20 @@ package ssl 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 { + 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) NewCreateInput() any @@ -30,9 +37,10 @@ func (c *SSLCustomCreateMgr) NewCreateInput() any { } func newAddCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *cobra.Command { - var path string + flags := &AddedFlags{} + 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,7 +52,18 @@ func newAddCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *cobra.Command input := sslType.managers.createMgr.NewCreateInput() - 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 { + required := []string{"name", "public-key", "private-key"} + if err := base.ValidateFlags(cmd, required); err != nil { + return err + } + } + + if err := flags.FillInput(cmd, input); err != nil { return err } @@ -64,10 +83,42 @@ 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(&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 (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 = f.Name + } + if cmd.Flags().Changed("public-key") { + sslInput.PublicKey = f.PublicKey + } + if cmd.Flags().Changed("private-key") { + sslInput.PrivateKey = f.PrivateKey + } + if cmd.Flags().Changed("chain-key") { + sslInput.ChainKey = f.ChainKey + } + if cmd.Flags().Changed("label") { + labelsMap, err := base.ParseLabels(f.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,