Repository: skupperproject/skupper (go)

Issue:
[v2] make name optional in kube token command 
currently name is required in token command.
skupper token issue <name> ~/token.yaml

The desire is to have the command  remove the required name and add an optional parameter to set name.
skupper token issue ~/token.yaml

The default name will be:
assigning a random UUID as the default name for example:
like west-70dc0eb4 (<sitename>-<randomvalue>). 

Pull request description:
V2 make name optional in token issue command
Make name optional in token issue command.

fixes #1640 

Patch:
diff --git a/cmd/skupper/README.md b/cmd/skupper/README.md
index 145c5c14..c961ccbc 100644
--- a/cmd/skupper/README.md
+++ b/cmd/skupper/README.md
@@ -67,7 +67,7 @@ skupper listener create backend 8080 --routing-key backend -n west
 Create a AccessGrant in west site and generate a file with the AccessToken:
 
 ```
-skupper token issue my-token ~/my-token.yaml -n west
+skupper token issue ~/my-token.yaml -n west
 ```
 
 Copy token.yaml file to east site and redeem:
diff --git a/internal/cmd/skupper/common/flags.go b/internal/cmd/skupper/common/flags.go
index fa3ed858..33befd22 100644
--- a/internal/cmd/skupper/common/flags.go
+++ b/internal/cmd/skupper/common/flags.go
@@ -42,6 +42,8 @@ for other Kubernetes flavors, loadbalancer is the default.`
 	FlagDescRedemptionsAllowed = "The number of times an access token for this grant can be redeemed."
 	FlagNameExpirationWindow   = "expiration-window"
 	FlagDescExpirationWindow   = "The period of time in which an access token for this grant can be redeemed."
+	FlagNameToken              = "name"
+	FlagDescToken              = "The name of token issued"
 
 	FlagNameRoutingKey      = "routing-key"
 	FlagDescRoutingKey      = "The identifier used to route traffic from listeners to connectors"
@@ -118,6 +120,7 @@ type CommandLinkStatusFlags struct {
 }
 
 type CommandTokenIssueFlags struct {
+	Name               string
 	Timeout            time.Duration
 	ExpirationWindow   time.Duration
 	RedemptionsAllowed int
diff --git a/internal/cmd/skupper/common/utils/status.go b/internal/cmd/skupper/common/utils/status.go
index 04a9377e..761c8bef 100644
--- a/internal/cmd/skupper/common/utils/status.go
+++ b/internal/cmd/skupper/common/utils/status.go
@@ -13,11 +13,11 @@ func SiteConfigured(siteList *v1alpha1.SiteList) bool {
 	return false
 }
 
-func SiteReady(siteList *v1alpha1.SiteList) bool {
+func SiteReady(siteList *v1alpha1.SiteList) (bool, string) {
 	for _, s := range siteList.Items {
 		if s.IsReady() {
-			return true
+			return true, s.Name
 		}
 	}
-	return false
+	return false, ""
 }
diff --git a/internal/cmd/skupper/token/kube/token_issue.go b/internal/cmd/skupper/token/kube/token_issue.go
index 02e7edbd..c5bd95ca 100644
--- a/internal/cmd/skupper/token/kube/token_issue.go
+++ b/internal/cmd/skupper/token/kube/token_issue.go
@@ -3,11 +3,13 @@ package kube
 import (
 	"context"
 	"fmt"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"os"
 	"time"
 
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
+
+	"github.com/google/uuid"
 	"github.com/skupperproject/skupper/internal/kube/client"
 	"github.com/skupperproject/skupper/pkg/apis/skupper/v1alpha1"
 	skupperv1alpha1 "github.com/skupperproject/skupper/pkg/generated/client/clientset/versioned/typed/skupper/v1alpha1"
@@ -45,28 +47,19 @@ func (cmd *CmdTokenIssue) ValidateInput(args []string) []error {
 	resourceStringValidator := validator.NewResourceStringValidator()
 	tokenStringValidator := validator.NewFilePathStringValidator()
 
-	// Validate grant name and token file name
-	if len(args) < 2 {
-		validationErrors = append(validationErrors, fmt.Errorf("token name and file name must be configured"))
-	} else if len(args) > 2 {
-		validationErrors = append(validationErrors, fmt.Errorf("only two arguments are allowed for this command"))
+	// Validate token file name
+	if len(args) < 1 {
+		validationErrors = append(validationErrors, fmt.Errorf("file name must be configured"))
+	} else if len(args) > 1 {
+		validationErrors = append(validationErrors, fmt.Errorf("only one argument is allowed for this command"))
 	} else if args[0] == "" {
-		validationErrors = append(validationErrors, fmt.Errorf("token name must not be empty"))
-	} else if args[1] == "" {
 		validationErrors = append(validationErrors, fmt.Errorf("file name must not be empty"))
 	} else {
-		ok, err := resourceStringValidator.Evaluate(args[0])
-		if !ok {
-			validationErrors = append(validationErrors, fmt.Errorf("token name is not valid: %s", err))
-		} else {
-			cmd.grantName = args[0]
-		}
-
-		ok, err = tokenStringValidator.Evaluate(args[1])
+		ok, err := tokenStringValidator.Evaluate(args[0])
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("token file name is not valid: %s", err))
 		} else {
-			cmd.fileName = args[1]
+			cmd.fileName = args[0]
 		}
 	}
 
@@ -75,8 +68,21 @@ func (cmd *CmdTokenIssue) ValidateInput(args []string) []error {
 	if siteList == nil || len(siteList.Items) == 0 {
 		validationErrors = append(validationErrors, fmt.Errorf("A site must exist in namespace %s before a token can be created", cmd.namespace))
 	} else {
-		if !utils.SiteReady(siteList) {
+		ok, siteName := utils.SiteReady(siteList)
+		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("there is no active skupper site in this namespace"))
+		} else {
+			// used configured name or generate a grant name
+			if cmd.Flags.Name != "" {
+				ok, err := resourceStringValidator.Evaluate(cmd.Flags.Name)
+				if !ok {
+					validationErrors = append(validationErrors, fmt.Errorf("token name is not valid: %s", err))
+				} else {
+					cmd.grantName = cmd.Flags.Name
+				}
+			} else {
+				cmd.grantName = siteName + "-" + uuid.New().String()
+			}
 		}
 	}
 
diff --git a/internal/cmd/skupper/token/kube/token_redeem.go b/internal/cmd/skupper/token/kube/token_redeem.go
index 3a4cfa81..072ebc5b 100644
--- a/internal/cmd/skupper/token/kube/token_redeem.go
+++ b/internal/cmd/skupper/token/kube/token_redeem.go
@@ -3,11 +3,12 @@ package kube
 import (
 	"context"
 	"fmt"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"os"
 	"time"
 
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
+
 	"github.com/skupperproject/skupper/internal/kube/client"
 	"github.com/skupperproject/skupper/pkg/apis/skupper/v1alpha1"
 	"github.com/skupperproject/skupper/pkg/generated/client/clientset/versioned/scheme"
@@ -64,7 +65,8 @@ func (cmd *CmdTokenRedeem) ValidateInput(args []string) []error {
 	if siteList == nil || len(siteList.Items) == 0 {
 		validationErrors = append(validationErrors, fmt.Errorf("A site must exist in namespace %s before a token can be redeemed", cmd.namespace))
 	} else {
-		if !utils.SiteReady(siteList) {
+		ok, _ := utils.SiteReady(siteList)
+		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("there is no active skupper site in this namespace"))
 		}
 	}
diff --git a/internal/cmd/skupper/token/token.go b/internal/cmd/skupper/token/token.go
index 62a4178f..ca3ec60e 100644
--- a/internal/cmd/skupper/token/token.go
+++ b/internal/cmd/skupper/token/token.go
@@ -1,11 +1,12 @@
 package token
 
 import (
+	"time"
+
 	"github.com/skupperproject/skupper/api/types"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/token/kube"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/token/nonkube"
-	"time"
 
 	"github.com/skupperproject/skupper/pkg/config"
 	"github.com/spf13/cobra"
@@ -34,10 +35,10 @@ func CmdTokenIssueFactory(configuredPlatform types.Platform) *cobra.Command {
 	nonKubeCommand := nonkube.NewCmdTokenIssue()
 
 	cmdTokenIssueDesc := common.SkupperCmdDescription{
-		Use:     "issue <name> <fileName>",
+		Use:     "issue <fileName>",
 		Short:   "issue a token",
 		Long:    "Issue a token file redeemable for a link to the current site.",
-		Example: "skupper token issue tokenName ~/token1.yaml",
+		Example: "skupper token issue ~/token1.yaml",
 	}
 
 	cmd := common.ConfigureCobraCommand(configuredPlatform, cmdTokenIssueDesc, kubeCommand, nonKubeCommand)
@@ -47,6 +48,7 @@ func CmdTokenIssueFactory(configuredPlatform types.Platform) *cobra.Command {
 	cmd.Flags().IntVarP(&cmdFlags.RedemptionsAllowed, common.FlagNameRedemptionsAllowed, "r", 1, common.FlagDescRedemptionsAllowed)
 	cmd.Flags().DurationVarP(&cmdFlags.ExpirationWindow, common.FlagNameExpirationWindow, "e", 15*time.Minute, common.FlagDescExpirationWindow)
 	cmd.Flags().DurationVarP(&cmdFlags.Timeout, common.FlagNameTimeout, "t", 60*time.Second, common.FlagDescTimeout)
+	cmd.Flags().StringVar(&cmdFlags.Name, common.FlagNameToken, "", common.FlagDescToken)
 
 	kubeCommand.CobraCmd = cmd
 	kubeCommand.Flags = &cmdFlags


Test patch:
diff --git a/internal/cmd/skupper/token/kube/token_issue_test.go b/internal/cmd/skupper/token/kube/token_issue_test.go
index 3d0f546a..3d3b9a2f 100644
--- a/internal/cmd/skupper/token/kube/token_issue_test.go
+++ b/internal/cmd/skupper/token/kube/token_issue_test.go
@@ -1,12 +1,13 @@
 package kube
 
 import (
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"os"
 	"testing"
 	"time"
 
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
+
 	fakeclient "github.com/skupperproject/skupper/internal/kube/client/fake"
 	"github.com/skupperproject/skupper/pkg/apis/skupper/v1alpha1"
 	"gotest.tools/assert"
@@ -27,11 +28,12 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 	testTable := []test{
 		{
 			name: "token is not issued because there is already the same token in the namespace",
-			args: []string{"my-token", "~/token.yaml"},
+			args: []string{"~/token.yaml"},
 			flags: common.CommandTokenIssueFlags{
 				ExpirationWindow:   15 * time.Minute,
 				RedemptionsAllowed: 1,
 				Timeout:            60 * time.Second,
+				Name:               "my-token",
 			},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.SiteList{
@@ -83,7 +85,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 		},
 		{
 			name: "token no site",
-			args: []string{"token", "filename"},
+			args: []string{"filename"},
 			flags: common.CommandTokenIssueFlags{
 				ExpirationWindow:   15 * time.Minute,
 				RedemptionsAllowed: 1,
@@ -111,7 +113,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 		},
 		{
 			name: "token no site with OK status",
-			args: []string{"token", "filename"},
+			args: []string{"filename"},
 			flags: common.CommandTokenIssueFlags{
 				ExpirationWindow:   15 * time.Minute,
 				RedemptionsAllowed: 1,
@@ -141,7 +143,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 			expectedErrors: []string{"there is no active skupper site in this namespace"},
 		},
 		{
-			name: "token name and file name are not specified",
+			name: "file name is not specified",
 			args: []string{},
 			flags: common.CommandTokenIssueFlags{
 				ExpirationWindow:   15 * time.Minute,
@@ -174,47 +176,11 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			expectedErrors: []string{"token name and file name must be configured"},
-		},
-		{
-			name: "token name empty",
-			args: []string{"", "~/token.yaml"},
-			flags: common.CommandTokenIssueFlags{
-				ExpirationWindow:   15 * time.Minute,
-				RedemptionsAllowed: 1,
-				Timeout:            60 * time.Second,
-			},
-			skupperObjects: []runtime.Object{
-				&v1alpha1.SiteList{
-					Items: []v1alpha1.Site{
-						{
-							ObjectMeta: v1.ObjectMeta{
-								Name:      "site1",
-								Namespace: "test",
-							},
-							Status: v1alpha1.SiteStatus{
-								Status: v1alpha1.Status{
-									Conditions: []v1.Condition{
-										{
-											Type:   "Configured",
-											Status: "True",
-										},
-										{
-											Type:   "Running",
-											Status: "True",
-										},
-									},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectedErrors: []string{"token name must not be empty"},
+			expectedErrors: []string{"file name must be configured"},
 		},
 		{
 			name: "token file name empty",
-			args: []string{"my-grant-file-empty", ""},
+			args: []string{""},
 			flags: common.CommandTokenIssueFlags{
 				ExpirationWindow:   15 * time.Minute,
 				RedemptionsAllowed: 1,
@@ -249,44 +215,8 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 			expectedErrors: []string{"file name must not be empty"},
 		},
 		{
-			name: "token filename is not specified",
-			args: []string{"my-name"},
-			flags: common.CommandTokenIssueFlags{
-				ExpirationWindow:   15 * time.Minute,
-				RedemptionsAllowed: 1,
-				Timeout:            60 * time.Second,
-			},
-			skupperObjects: []runtime.Object{
-				&v1alpha1.SiteList{
-					Items: []v1alpha1.Site{
-						{
-							ObjectMeta: v1.ObjectMeta{
-								Name:      "site1",
-								Namespace: "test",
-							},
-							Status: v1alpha1.SiteStatus{
-								Status: v1alpha1.Status{
-									Conditions: []v1.Condition{
-										{
-											Type:   "Configured",
-											Status: "True",
-										},
-										{
-											Type:   "Running",
-											Status: "True",
-										},
-									},
-								},
-							},
-						},
-					},
-				},
-			},
-			expectedErrors: []string{"token name and file name must be configured"},
-		},
-		{
-			name: "more than two arguments are specified",
-			args: []string{"my-grant", "/home/user/my-grant.yaml", "test"},
+			name: "more than one arguments is specified",
+			args: []string{"/home/user/my-grant.yaml", "test"},
 			flags: common.CommandTokenIssueFlags{
 				ExpirationWindow:   15 * time.Minute,
 				RedemptionsAllowed: 1,
@@ -318,15 +248,16 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			expectedErrors: []string{"only two arguments are allowed for this command"},
+			expectedErrors: []string{"only one argument is allowed for this command"},
 		},
 		{
 			name: "token name is not valid.",
-			args: []string{"my new token", "~/token.yaml"},
+			args: []string{"~/token.yaml"},
 			flags: common.CommandTokenIssueFlags{
 				ExpirationWindow:   15 * time.Minute,
 				RedemptionsAllowed: 1,
 				Timeout:            60 * time.Second,
+				Name:               "my new token",
 			},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.SiteList{
@@ -358,7 +289,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 		},
 		{
 			name: "token file name is not valid.",
-			args: []string{"my-grant", "ab cd"},
+			args: []string{"ab cd"},
 			flags: common.CommandTokenIssueFlags{
 				ExpirationWindow:   15 * time.Minute,
 				RedemptionsAllowed: 1,
@@ -394,7 +325,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 		},
 		{
 			name: "redemptions is not valid",
-			args: []string{"my-token-redemptions", "~/token.yaml"},
+			args: []string{"~/token.yaml"},
 			flags: common.CommandTokenIssueFlags{
 				ExpirationWindow:   15 * time.Minute,
 				RedemptionsAllowed: 0,
@@ -431,7 +362,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 		},
 		{
 			name: "expiration is not valid",
-			args: []string{"my-token-expiration", "~/token.yaml"},
+			args: []string{"~/token.yaml"},
 			flags: common.CommandTokenIssueFlags{
 				ExpirationWindow:   0 * time.Minute,
 				RedemptionsAllowed: 1,
@@ -467,7 +398,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 		},
 		{
 			name: "timeout is not valid",
-			args: []string{"token-timeout", "~/token.yaml"},
+			args: []string{"~/token.yaml"},
 			flags: common.CommandTokenIssueFlags{
 				ExpirationWindow:   15 * time.Minute,
 				RedemptionsAllowed: 1,
@@ -503,7 +434,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 		},
 		{
 			name: "flags all valid",
-			args: []string{"my-token-flags", "~/token.yaml"},
+			args: []string{"~/token.yaml"},
 			flags: common.CommandTokenIssueFlags{
 				ExpirationWindow:   15 * time.Minute,
 				RedemptionsAllowed: 1,
diff --git a/internal/cmd/skupper/token/token_test.go b/internal/cmd/skupper/token/token_test.go
index cf05b048..4a7f451e 100644
--- a/internal/cmd/skupper/token/token_test.go
+++ b/internal/cmd/skupper/token/token_test.go
@@ -2,12 +2,13 @@ package token
 
 import (
 	"fmt"
+	"testing"
+
 	"github.com/skupperproject/skupper/api/types"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/spf13/cobra"
 	"github.com/spf13/pflag"
 	"gotest.tools/assert"
-	"testing"
 )
 
 func TestCmdTokenFactory(t *testing.T) {
@@ -25,6 +26,7 @@ func TestCmdTokenFactory(t *testing.T) {
 				common.FlagNameTimeout:            "1m0s",
 				common.FlagNameExpirationWindow:   "15m0s",
 				common.FlagNameRedemptionsAllowed: "1",
+				common.FlagNameToken:              "",
 			},
 			command: CmdTokenIssueFactory(types.PlatformKubernetes),
 		},