Repository: skupperproject/skupper
Pull request #1663 (2024-09-17 22:31:43)

V2 Default values for listener and connector commands
Default routing-key in listener and connector commands
Default selector in connector command

if routing-key flag is not set then set the routing-key to equal the name of the listener or connector.  If selector, host or workload are not set in the connector command default to selector to equal "app=connector-name".

nluaces: >If selector, host or workload are not set in the connector command default to selector to equal "app=".

Do you mean "app=connector-name", right?

Patch:
diff --git a/cmd/skupper/README.md b/cmd/skupper/README.md
index 145c5c14..9fdcf93c 100644
--- a/cmd/skupper/README.md
+++ b/cmd/skupper/README.md
@@ -53,13 +53,13 @@ skupper site create site2  -n east
 # Expose backend in east site (create a connector)
 
 ```
-skupper connector create backend 8080 --routing-key backend --selector app=backend -n east
+skupper connector create backend 8080 -n east
 ```
 
 # Consume backend in west site (create a listener)
 
 ```
-skupper listener create backend 8080 --routing-key backend -n west
+skupper listener create backend 8080 -n west
 ```
 
 # Link sites
@@ -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/connector/kube/connector_create.go b/internal/cmd/skupper/connector/kube/connector_create.go
index ce71b012..51c7739b 100644
--- a/internal/cmd/skupper/connector/kube/connector_create.go
+++ b/internal/cmd/skupper/connector/kube/connector_create.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"
 	"strconv"
 	"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"
 	skupperv1alpha1 "github.com/skupperproject/skupper/pkg/generated/client/clientset/versioned/typed/skupper/v1alpha1"
@@ -18,27 +19,22 @@ import (
 	"k8s.io/client-go/kubernetes"
 )
 
-type ConnectorCreate struct {
-	routingKey      string
+type CmdConnectorCreate struct {
+	client          skupperv1alpha1.SkupperV1alpha1Interface
+	CobraCmd        *cobra.Command
+	Flags           *common.CommandConnectorCreateFlags
+	namespace       string
+	name            string
+	port            int
+	output          string
 	host            string
 	selector        string
 	tlsSecret       string
+	routingKey      string
 	connectorType   string
 	includeNotReady bool
-	workload        string
 	timeout         time.Duration
-	output          string
-}
-
-type CmdConnectorCreate struct {
-	client     skupperv1alpha1.SkupperV1alpha1Interface
-	CobraCmd   *cobra.Command
-	Flags      *common.CommandConnectorCreateFlags
-	namespace  string
-	name       string
-	port       int
-	output     string
-	KubeClient kubernetes.Interface
+	KubeClient      kubernetes.Interface
 }
 
 func NewCmdConnectorCreate() *CmdConnectorCreate {
@@ -62,7 +58,6 @@ func (cmd *CmdConnectorCreate) ValidateInput(args []string) []error {
 	connectorTypeValidator := validator.NewOptionValidator(common.ConnectorTypes)
 	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
 	workloadStringValidator := validator.NewWorkloadStringValidator()
-	numTargetTypesSelected := 0
 
 	// Validate arguments name and port
 	if len(args) < 2 {
@@ -116,8 +111,6 @@ func (cmd *CmdConnectorCreate) ValidateInput(args []string) []error {
 			validationErrors = append(validationErrors, fmt.Errorf("routing key is not valid: %s", err))
 		}
 	}
-
-	//TBD what characters are not allowed for host flag
 	if cmd.Flags.TlsSecret != "" {
 		// check that the secret exists
 		_, err := cmd.KubeClient.CoreV1().Secrets(cmd.namespace).Get(context.TODO(), cmd.Flags.TlsSecret, metav1.GetOptions{})
@@ -131,16 +124,26 @@ func (cmd *CmdConnectorCreate) ValidateInput(args []string) []error {
 			validationErrors = append(validationErrors, fmt.Errorf("connector type is not valid: %s", err))
 		}
 	}
+	// only one of workload, selector or host can be specified
+	if cmd.Flags.Host != "" {
+		if cmd.Flags.Workload != "" || cmd.Flags.Selector != "" {
+			validationErrors = append(validationErrors, fmt.Errorf("If host is configured, cannot configure workload or selector"))
+		}
+		//TBD what characters are not allowed for host flag
+	}
 	if cmd.Flags.Selector != "" {
-		numTargetTypesSelected++
+		if cmd.Flags.Workload != "" || cmd.Flags.Host != "" {
+			validationErrors = append(validationErrors, fmt.Errorf("If selector is configured, cannot configure workload or host"))
+		}
 		ok, err := workloadStringValidator.Evaluate(cmd.Flags.Selector)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("selector is not valid: %s", err))
 		}
 	}
-	//TBD no workload in connector CRD
 	if cmd.Flags.Workload != "" {
-		numTargetTypesSelected++
+		if cmd.Flags.Selector != "" || cmd.Flags.Host != "" {
+			validationErrors = append(validationErrors, fmt.Errorf("If workload is configured, cannot configure selector or host"))
+		}
 		ok, err := workloadStringValidator.Evaluate(cmd.Flags.Workload)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("workload is not valid: %s", err))
@@ -150,28 +153,42 @@ func (cmd *CmdConnectorCreate) ValidateInput(args []string) []error {
 	if cmd.Flags.Timeout <= 0*time.Minute {
 		validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid"))
 	}
-
-	if cmd.Flags.Host != "" {
-		numTargetTypesSelected++
-	}
-	// workload, selector or host must be specified
-	if numTargetTypesSelected == 0 {
-		validationErrors = append(validationErrors, fmt.Errorf("One of the following options must be set: workload, selector, host"))
-	} else if numTargetTypesSelected > 1 {
-		validationErrors = append(validationErrors, fmt.Errorf("Only one of the following options must be set: workload, selector, host"))
-	}
-
 	if cmd.Flags.Output != "" {
 		ok, err := outputTypeValidator.Evaluate(cmd.Flags.Output)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("output type is not valid: %s", err))
-		} else {
-			cmd.output = cmd.Flags.Output
 		}
 	}
 	return validationErrors
 }
 
+func (cmd *CmdConnectorCreate) InputToOptions() {
+
+	// workload, selector or host must be specified
+	if cmd.Flags.Workload == "" && cmd.Flags.Selector == "" && cmd.Flags.Host == "" {
+		// default selector to name of connector
+		cmd.selector = "app=" + cmd.name
+	}
+	if cmd.Flags.Host != "" {
+		cmd.host = cmd.Flags.Host
+	}
+	if cmd.Flags.Selector != "" {
+		cmd.selector = cmd.Flags.Selector
+	}
+
+	// default routingkey to name of connector
+	if cmd.Flags.RoutingKey == "" {
+		cmd.routingKey = cmd.name
+	} else {
+		cmd.routingKey = cmd.Flags.RoutingKey
+	}
+	cmd.timeout = cmd.Flags.Timeout
+	cmd.tlsSecret = cmd.Flags.TlsSecret
+	cmd.connectorType = cmd.Flags.ConnectorType
+	cmd.output = cmd.Flags.Output
+	cmd.includeNotReady = cmd.Flags.IncludeNotReady
+}
+
 func (cmd *CmdConnectorCreate) Run() error {
 
 	resource := v1alpha1.Connector{
@@ -184,14 +201,13 @@ func (cmd *CmdConnectorCreate) Run() error {
 			Namespace: cmd.namespace,
 		},
 		Spec: v1alpha1.ConnectorSpec{
-			Host:            cmd.Flags.Host,
+			Host:            cmd.host,
 			Port:            cmd.port,
-			RoutingKey:      cmd.Flags.RoutingKey,
-			TlsCredentials:  cmd.Flags.TlsSecret,
-			Type:            cmd.Flags.ConnectorType,
-			IncludeNotReady: cmd.Flags.IncludeNotReady,
-			Selector:        cmd.Flags.Selector,
-			//Workload:       cmd.Flags.workload,
+			RoutingKey:      cmd.routingKey,
+			TlsCredentials:  cmd.tlsSecret,
+			Type:            cmd.connectorType,
+			IncludeNotReady: cmd.includeNotReady,
+			Selector:        cmd.selector,
 		},
 	}
 
@@ -211,7 +227,7 @@ func (cmd *CmdConnectorCreate) WaitUntil() error {
 		return nil
 	}
 
-	waitTime := int(cmd.Flags.Timeout.Seconds())
+	waitTime := int(cmd.timeout.Seconds())
 	err := utils.NewSpinnerWithTimeout("Waiting for create to complete...", waitTime, func() error {
 
 		resource, err := cmd.client.Connectors(cmd.namespace).Get(context.TODO(), cmd.name, metav1.GetOptions{})
@@ -233,5 +249,3 @@ func (cmd *CmdConnectorCreate) WaitUntil() error {
 	fmt.Printf("Connector %q is ready\n", cmd.name)
 	return nil
 }
-
-func (cmd *CmdConnectorCreate) InputToOptions() {}
diff --git a/internal/cmd/skupper/connector/kube/connector_update.go b/internal/cmd/skupper/connector/kube/connector_update.go
index 5f19896b..0e751fbb 100644
--- a/internal/cmd/skupper/connector/kube/connector_update.go
+++ b/internal/cmd/skupper/connector/kube/connector_update.go
@@ -3,9 +3,10 @@ package kube
 import (
 	"context"
 	"fmt"
+	"time"
+
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
-	"time"
 
 	"github.com/skupperproject/skupper/internal/kube/client"
 	"github.com/skupperproject/skupper/pkg/apis/skupper/v1alpha1"
@@ -63,7 +64,6 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) []error {
 	connectorTypeValidator := validator.NewOptionValidator(common.ConnectorTypes)
 	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
 	workloadStringValidator := validator.NewWorkloadStringValidator()
-	numTargetTypesSelected := 0
 
 	// Validate arguments name
 	if len(args) < 1 {
@@ -93,7 +93,6 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) []error {
 			cmd.newSettings.port = connector.Spec.Port
 			cmd.newSettings.tlsSecret = connector.Spec.TlsCredentials
 			cmd.newSettings.connectorType = connector.Spec.Type
-			//cmd.newSettings.workload = connector.Spec.Workload
 			cmd.newSettings.selector = connector.Spec.Selector
 			cmd.newSettings.includeNotReady = connector.Spec.IncludeNotReady
 		}
@@ -108,11 +107,6 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) []error {
 			cmd.newSettings.routingKey = cmd.Flags.RoutingKey
 		}
 	}
-	//TBD what characters are not allowed for host flag
-	if cmd.Flags.Host != "" {
-		numTargetTypesSelected++
-		cmd.newSettings.host = cmd.Flags.Host
-	}
 	if cmd.Flags.TlsSecret != "" {
 		_, err := cmd.KubeClient.CoreV1().Secrets(cmd.namespace).Get(context.TODO(), cmd.Flags.TlsSecret, metav1.GetOptions{})
 		if err != nil {
@@ -137,9 +131,16 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) []error {
 			cmd.newSettings.port = cmd.Flags.Port
 		}
 	}
+	//TBD what is valid timeout ---> use timevalidator
+	if cmd.Flags.Timeout <= 0*time.Minute {
+		validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid"))
+	}
+	//TBD what characters are not allowed for host flag
+	if cmd.Flags.Host != "" {
+		cmd.newSettings.host = cmd.Flags.Host
+	}
 	//TBD what are valid values here
 	if cmd.Flags.Selector != "" {
-		numTargetTypesSelected++
 		ok, err := workloadStringValidator.Evaluate(cmd.Flags.Selector)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("selector is not valid: %s", err))
@@ -151,11 +152,23 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) []error {
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("workload is not valid: %s", err))
 		}
-		cmd.newSettings.selector = cmd.Flags.Workload
+		cmd.newSettings.workload = cmd.Flags.Workload
 	}
-	//TBD what is valid timeout ---> use timevalidator
-	if cmd.Flags.Timeout <= 0*time.Minute {
-		validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid"))
+	//Can only have one workload/Selector/host set
+	if cmd.newSettings.host != "" {
+		if cmd.newSettings.workload != "" || cmd.newSettings.selector != "" {
+			validationErrors = append(validationErrors, fmt.Errorf("If host is configured, cannot configure workload or selector"))
+		}
+	}
+	if cmd.newSettings.selector != "" {
+		if cmd.newSettings.host != "" || cmd.newSettings.workload != "" {
+			validationErrors = append(validationErrors, fmt.Errorf("If selector is configured, cannot configure workload or host"))
+		}
+	}
+	if cmd.newSettings.workload != "" {
+		if cmd.newSettings.selector != "" || cmd.newSettings.host != "" {
+			validationErrors = append(validationErrors, fmt.Errorf("If workload is configured, cannot configure selector or host"))
+		}
 	}
 	if cmd.Flags.Output != "" {
 		ok, err := outputTypeValidator.Evaluate(cmd.Flags.Output)
@@ -166,14 +179,6 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) []error {
 		}
 	}
 
-	if cmd.Flags.Host != "" {
-		numTargetTypesSelected++
-	}
-
-	if numTargetTypesSelected > 1 {
-		validationErrors = append(validationErrors, fmt.Errorf("only one of --host, --selector, or --workload can be specified"))
-	}
-
 	return validationErrors
 }
 
@@ -190,12 +195,11 @@ func (cmd *CmdConnectorUpdate) Run() error {
 			ResourceVersion: cmd.resourceVersion,
 		},
 		Spec: v1alpha1.ConnectorSpec{
-			Host:           cmd.newSettings.host,
-			Port:           cmd.newSettings.port,
-			RoutingKey:     cmd.newSettings.routingKey,
-			TlsCredentials: cmd.newSettings.tlsSecret,
-			Type:           cmd.newSettings.connectorType,
-			//Workload:       cmd.newSettings.workload, //TODO: if this flag is not used we should reconsider its removal
+			Host:            cmd.newSettings.host,
+			Port:            cmd.newSettings.port,
+			RoutingKey:      cmd.newSettings.routingKey,
+			TlsCredentials:  cmd.newSettings.tlsSecret,
+			Type:            cmd.newSettings.connectorType,
 			Selector:        cmd.newSettings.selector,
 			IncludeNotReady: cmd.newSettings.includeNotReady,
 		},
diff --git a/internal/cmd/skupper/listener/kube/listener_create.go b/internal/cmd/skupper/listener/kube/listener_create.go
index 7e192205..7131dadc 100644
--- a/internal/cmd/skupper/listener/kube/listener_create.go
+++ b/internal/cmd/skupper/listener/kube/listener_create.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"
 	"strconv"
 	"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"
 	skupperv1alpha1 "github.com/skupperproject/skupper/pkg/generated/client/clientset/versioned/typed/skupper/v1alpha1"
@@ -18,24 +19,20 @@ import (
 	"k8s.io/client-go/kubernetes"
 )
 
-type ListenerCreate struct {
-	routingKey   string
+type CmdListenerCreate struct {
+	client       skupperv1alpha1.SkupperV1alpha1Interface
+	CobraCmd     *cobra.Command
+	Flags        *common.CommandListenerCreateFlags
+	namespace    string
+	name         string
+	port         int
 	host         string
 	tlsSecret    string
 	listenerType string
+	routingKey   string
 	timeout      time.Duration
 	output       string
-}
-
-type CmdListenerCreate struct {
-	client     skupperv1alpha1.SkupperV1alpha1Interface
-	CobraCmd   *cobra.Command
-	Flags      *common.CommandListenerCreateFlags
-	namespace  string
-	name       string
-	port       int
-	output     string
-	KubeClient kubernetes.Interface
+	KubeClient   kubernetes.Interface
 }
 
 func NewCmdListenerCreate() *CmdListenerCreate {
@@ -139,13 +136,29 @@ func (cmd *CmdListenerCreate) ValidateInput(args []string) []error {
 		ok, err := outputTypeValidator.Evaluate(cmd.Flags.Output)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("output type is not valid: %s", err))
-		} else {
-			cmd.output = cmd.Flags.Output
 		}
 	}
 	return validationErrors
 }
 
+func (cmd *CmdListenerCreate) InputToOptions() {
+	// default host and routingkey to name of listener
+	if cmd.Flags.Host == "" {
+		cmd.host = cmd.name
+	} else {
+		cmd.host = cmd.Flags.Host
+	}
+	if cmd.Flags.RoutingKey == "" {
+		cmd.routingKey = cmd.name
+	} else {
+		cmd.routingKey = cmd.Flags.RoutingKey
+	}
+	cmd.timeout = cmd.Flags.Timeout
+	cmd.tlsSecret = cmd.Flags.TlsSecret
+	cmd.listenerType = cmd.Flags.ListenerType
+	cmd.output = cmd.Flags.Output
+}
+
 func (cmd *CmdListenerCreate) Run() error {
 
 	resource := v1alpha1.Listener{
@@ -158,11 +171,11 @@ func (cmd *CmdListenerCreate) Run() error {
 			Namespace: cmd.namespace,
 		},
 		Spec: v1alpha1.ListenerSpec{
-			Host:           cmd.Flags.Host,
+			Host:           cmd.host,
 			Port:           cmd.port,
-			RoutingKey:     cmd.Flags.RoutingKey,
-			TlsCredentials: cmd.Flags.TlsSecret,
-			Type:           cmd.Flags.ListenerType,
+			RoutingKey:     cmd.routingKey,
+			TlsCredentials: cmd.tlsSecret,
+			Type:           cmd.listenerType,
 		},
 	}
 
@@ -182,7 +195,7 @@ func (cmd *CmdListenerCreate) WaitUntil() error {
 		return nil
 	}
 
-	waitTime := int(cmd.Flags.Timeout.Seconds())
+	waitTime := int(cmd.timeout.Seconds())
 	err := utils.NewSpinnerWithTimeout("Waiting for create to complete...", waitTime, func() error {
 
 		resource, err := cmd.client.Listeners(cmd.namespace).Get(context.TODO(), cmd.name, metav1.GetOptions{})
@@ -204,5 +217,3 @@ func (cmd *CmdListenerCreate) WaitUntil() error {
 	fmt.Printf("Listener %q is ready\n", cmd.name)
 	return nil
 }
-
-func (cmd *CmdListenerCreate) InputToOptions() {}
diff --git a/internal/cmd/skupper/site/kube/site_create.go b/internal/cmd/skupper/site/kube/site_create.go
index 9c5c41c7..9c8028d9 100644
--- a/internal/cmd/skupper/site/kube/site_create.go
+++ b/internal/cmd/skupper/site/kube/site_create.go
@@ -6,6 +6,8 @@ package kube
 import (
 	"context"
 	"fmt"
+	"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"
@@ -15,7 +17,6 @@ import (
 	"github.com/spf13/cobra"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/kubernetes"
-	"time"
 )
 
 type CmdSiteCreate struct {
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/connector/kube/connector_create_test.go b/internal/cmd/skupper/connector/kube/connector_create_test.go
index ab8d80c4..7ffe0801 100644
--- a/internal/cmd/skupper/connector/kube/connector_create_test.go
+++ b/internal/cmd/skupper/connector/kube/connector_create_test.go
@@ -1,11 +1,12 @@
 package kube
 
 import (
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"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"
@@ -614,10 +615,13 @@ func TestCmdConnectorCreate_ValidateInput(t *testing.T) {
 				"output type is not valid: value not-supported not allowed. It should be one of this options: [json yaml]"},
 		},
 		{
-			name: "missing selector/host/workload",
-			args: []string{"missing", "1234"}, flags: common.CommandConnectorCreateFlags{
-				Timeout: 1 * time.Second,
-				Output:  "json",
+			name: "selector/host",
+			args: []string{"selector", "1234"},
+			flags: common.CommandConnectorCreateFlags{
+				Timeout:  1 * time.Second,
+				Output:   "json",
+				Selector: "app=test",
+				Host:     "test",
 			},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.SiteList{
@@ -642,19 +646,17 @@ func TestCmdConnectorCreate_ValidateInput(t *testing.T) {
 				},
 			},
 			expectedErrors: []string{
-				"One of the following options must be set: workload, selector, host"},
+				"If host is configured, cannot configure workload or selector",
+				"If selector is configured, cannot configure workload or host"},
 		},
 		{
-			name: "flags all valid",
-			args: []string{"my-connector-flags", "8080"},
+			name: "workload/host",
+			args: []string{"workload", "1234"},
 			flags: common.CommandConnectorCreateFlags{
-				RoutingKey:      "routingkeyname",
-				TlsSecret:       "secretname",
-				ConnectorType:   "tcp",
-				Selector:        "backend",
-				IncludeNotReady: true,
-				Timeout:         30 * time.Second,
-				Output:          "json",
+				Timeout:  1 * time.Second,
+				Output:   "json",
+				Workload: "deployment/test",
+				Host:     "test",
 			},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.SiteList{
@@ -678,25 +680,17 @@ func TestCmdConnectorCreate_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			k8sObjects: []runtime.Object{
-				&v12.Secret{
-					ObjectMeta: v1.ObjectMeta{
-						Name:      "secretname",
-						Namespace: "test",
-					},
-				},
-			},
-			expectedErrors: []string{},
+			expectedErrors: []string{
+				"If host is configured, cannot configure workload or selector",
+				"If workload is configured, cannot configure selector or host"},
 		},
 		{
-			name: "more than one target type was selected",
+			name: "flags all valid",
 			args: []string{"my-connector-flags", "8080"},
 			flags: common.CommandConnectorCreateFlags{
-				Host:            "hostname",
 				RoutingKey:      "routingkeyname",
 				TlsSecret:       "secretname",
 				ConnectorType:   "tcp",
-				Selector:        "backend",
 				IncludeNotReady: true,
 				Timeout:         30 * time.Second,
 				Output:          "json",
@@ -731,7 +725,7 @@ func TestCmdConnectorCreate_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			expectedErrors: []string{"Only one of the following options must be set: workload, selector, host"},
+			expectedErrors: []string{},
 		},
 	}
 
@@ -753,6 +747,79 @@ func TestCmdConnectorCreate_ValidateInput(t *testing.T) {
 	}
 }
 
+func TestCmdConnectorCreate_InputToOptions(t *testing.T) {
+
+	type test struct {
+		name                  string
+		flags                 common.CommandConnectorCreateFlags
+		Connectorname         string
+		expectedTlsSecret     string
+		expectedHost          string
+		expectedSelector      string
+		expectedRoutingKey    string
+		expectedConnectorType string
+		expectedOutput        string
+		expectedTimeout       time.Duration
+	}
+
+	testTable := []test{
+		{
+			name:                  "test1",
+			flags:                 common.CommandConnectorCreateFlags{"backend", "", "app=backend", "secret", "tcp", true, "", 2 * time.Second, "json"},
+			expectedTlsSecret:     "secret",
+			expectedHost:          "",
+			expectedRoutingKey:    "backend",
+			expectedTimeout:       2 * time.Second,
+			expectedConnectorType: "tcp",
+			expectedOutput:        "json",
+			expectedSelector:      "app=backend",
+		},
+		{
+			name:                  "test2",
+			flags:                 common.CommandConnectorCreateFlags{"backend", "backend", "", "secret", "tcp", true, "", 2 * time.Second, "json"},
+			expectedTlsSecret:     "secret",
+			expectedHost:          "backend",
+			expectedRoutingKey:    "backend",
+			expectedTimeout:       2 * time.Second,
+			expectedConnectorType: "tcp",
+			expectedOutput:        "json",
+			expectedSelector:      "",
+		},
+		{
+			name:                  "test3",
+			flags:                 common.CommandConnectorCreateFlags{"", "", "", "secret", "tcp", false, "", 3 * time.Second, "yaml"},
+			expectedTlsSecret:     "secret",
+			expectedHost:          "",
+			expectedRoutingKey:    "test3",
+			expectedTimeout:       3 * time.Second,
+			expectedConnectorType: "tcp",
+			expectedOutput:        "yaml",
+			expectedSelector:      "app=test3",
+		},
+	}
+
+	for _, test := range testTable {
+		t.Run(test.name, func(t *testing.T) {
+
+			cmd, err := newCmdConnectorCreateWithMocks("test", nil, nil, "")
+			assert.Assert(t, err)
+
+			cmd.Flags = &test.flags
+			cmd.name = test.name
+
+			cmd.InputToOptions()
+
+			assert.Check(t, cmd.routingKey == test.expectedRoutingKey)
+			assert.Check(t, cmd.output == test.expectedOutput)
+			assert.Check(t, cmd.tlsSecret == test.expectedTlsSecret)
+			assert.Check(t, cmd.host == test.expectedHost)
+			assert.Check(t, cmd.timeout == test.expectedTimeout)
+			assert.Check(t, cmd.selector == test.expectedSelector)
+			assert.Check(t, cmd.connectorType == test.expectedConnectorType)
+		})
+	}
+}
+
 func TestCmdConnectorCreate_Run(t *testing.T) {
 	type test struct {
 		name                string
@@ -772,12 +839,10 @@ func TestCmdConnectorCreate_Run(t *testing.T) {
 			connectorPort: 8080,
 			flags: common.CommandConnectorCreateFlags{
 				ConnectorType:   "tcp",
-				Host:            "hostname",
 				RoutingKey:      "keyname",
 				TlsSecret:       "secretname",
 				IncludeNotReady: true,
-				Workload:        "deployment/backend",
-				Selector:        "backend",
+				Selector:        "app=backend",
 				Timeout:         1 * time.Second,
 			},
 		},
@@ -791,8 +856,6 @@ func TestCmdConnectorCreate_Run(t *testing.T) {
 				RoutingKey:      "keyname",
 				TlsSecret:       "secretname",
 				IncludeNotReady: true,
-				Workload:        "deployment/backend",
-				Selector:        "backend",
 				Timeout:         1 * time.Second,
 				Output:          "json",
 			},
@@ -901,8 +964,8 @@ func TestCmdConnectorCreate_WaitUntil(t *testing.T) {
 		assert.Assert(t, err)
 
 		cmd.name = "my-connector"
-		cmd.Flags = &common.CommandConnectorCreateFlags{Timeout: 1 * time.Second}
 		cmd.output = test.output
+		cmd.timeout = 1 * time.Second
 		cmd.namespace = "test"
 
 		t.Run(test.name, func(t *testing.T) {
diff --git a/internal/cmd/skupper/connector/kube/connector_update_test.go b/internal/cmd/skupper/connector/kube/connector_update_test.go
index 750ea38e..62ab0aed 100644
--- a/internal/cmd/skupper/connector/kube/connector_update_test.go
+++ b/internal/cmd/skupper/connector/kube/connector_update_test.go
@@ -1,11 +1,12 @@
 package kube
 
 import (
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"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"
@@ -223,16 +224,18 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 				"selector is not valid: value does not match this regular expression: ^[A-Za-z0-9=:./-]+$"},
 		},
 		{
-			name: "timeout is not valid",
-			args: []string{"bad-timeout"},
+			name: "selector/host",
+			args: []string{"selector"},
 			flags: common.CommandConnectorUpdateFlags{
-				Selector: "selector",
-				Timeout:  0 * time.Second,
+				Timeout:  1 * time.Second,
+				Output:   "json",
+				Selector: "app=test",
+				Host:     "test",
 			},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Connector{
 					ObjectMeta: v1.ObjectMeta{
-						Name:      "bad-timeout",
+						Name:      "selector",
 						Namespace: "test",
 					},
 					Status: v1alpha1.ConnectorStatus{
@@ -247,19 +250,23 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			expectedErrors: []string{"timeout is not valid"},
+			expectedErrors: []string{
+				"If host is configured, cannot configure workload or selector",
+				"If selector is configured, cannot configure workload or host"},
 		},
 		{
-			name: "output is not valid",
-			args: []string{"bad-output"},
+			name: "workload/host",
+			args: []string{"workload"},
 			flags: common.CommandConnectorUpdateFlags{
-				Output:  "not-supported",
-				Timeout: 1 * time.Minute,
+				Timeout:  1 * time.Second,
+				Output:   "json",
+				Workload: "deployment/test",
+				Host:     "test",
 			},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Connector{
 					ObjectMeta: v1.ObjectMeta{
-						Name:      "bad-output",
+						Name:      "workload",
 						Namespace: "test",
 					},
 					Status: v1alpha1.ConnectorStatus{
@@ -275,25 +282,20 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 				},
 			},
 			expectedErrors: []string{
-				"output type is not valid: value not-supported not allowed. It should be one of this options: [json yaml]"},
+				"If host is configured, cannot configure workload or selector",
+				"If workload is configured, cannot configure selector or host"},
 		},
 		{
-			name: "flags all valid",
-			args: []string{"my-connector-flags"},
+			name: "timeout is not valid",
+			args: []string{"bad-timeout"},
 			flags: common.CommandConnectorUpdateFlags{
-				RoutingKey:      "routingkeyname",
-				TlsSecret:       "secretname",
-				Port:            1234,
-				ConnectorType:   "tcp",
-				Selector:        "backend",
-				IncludeNotReady: false,
-				Timeout:         5 * time.Second,
-				Output:          "json",
+				Selector: "selector",
+				Timeout:  0 * time.Second,
 			},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Connector{
 					ObjectMeta: v1.ObjectMeta{
-						Name:      "my-connector-flags",
+						Name:      "bad-timeout",
 						Namespace: "test",
 					},
 					Status: v1alpha1.ConnectorStatus{
@@ -308,26 +310,44 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			k8sObjects: []runtime.Object{
-				&v12.Secret{
+			expectedErrors: []string{"timeout is not valid"},
+		},
+		{
+			name: "output is not valid",
+			args: []string{"bad-output"},
+			flags: common.CommandConnectorUpdateFlags{
+				Output:  "not-supported",
+				Timeout: 1 * time.Minute,
+			},
+			skupperObjects: []runtime.Object{
+				&v1alpha1.Connector{
 					ObjectMeta: v1.ObjectMeta{
-						Name:      "secretname",
+						Name:      "bad-output",
 						Namespace: "test",
 					},
+					Status: v1alpha1.ConnectorStatus{
+						Status: v1alpha1.Status{
+							Conditions: []v1.Condition{
+								{
+									Type:   "Configured",
+									Status: "True",
+								},
+							},
+						},
+					},
 				},
 			},
-			expectedErrors: []string{},
+			expectedErrors: []string{
+				"output type is not valid: value not-supported not allowed. It should be one of this options: [json yaml]"},
 		},
 		{
-			name: "more than one target type was selected",
+			name: "flags all valid",
 			args: []string{"my-connector-flags"},
 			flags: common.CommandConnectorUpdateFlags{
-				Host:            "hostname",
 				RoutingKey:      "routingkeyname",
 				TlsSecret:       "secretname",
 				Port:            1234,
 				ConnectorType:   "tcp",
-				Selector:        "backend",
 				IncludeNotReady: false,
 				Timeout:         5 * time.Second,
 				Output:          "json",
@@ -358,7 +378,7 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			expectedErrors: []string{"only one of --host, --selector, or --workload can be specified"},
+			expectedErrors: []string{},
 		},
 	}
 
diff --git a/internal/cmd/skupper/listener/kube/listener_create_test.go b/internal/cmd/skupper/listener/kube/listener_create_test.go
index feff6130..23aacbdf 100644
--- a/internal/cmd/skupper/listener/kube/listener_create_test.go
+++ b/internal/cmd/skupper/listener/kube/listener_create_test.go
@@ -1,11 +1,12 @@
 package kube
 
 import (
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"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"
@@ -589,6 +590,65 @@ func TestCmdListenerCreate_ValidateInput(t *testing.T) {
 	}
 }
 
+func TestCmdListenerCreate_InputToOptions(t *testing.T) {
+
+	type test struct {
+		name                 string
+		flags                common.CommandListenerCreateFlags
+		Listenername         string
+		expectedTlsSecret    string
+		expectedHost         string
+		expectedRoutingKey   string
+		expectedListenerType string
+		expectedOutput       string
+		expectedTimeout      time.Duration
+	}
+
+	testTable := []test{
+		{
+			name:                 "test1",
+			flags:                common.CommandListenerCreateFlags{"backend", "backend", "secret", "tcp", 2 * time.Second, "json"},
+			expectedTlsSecret:    "secret",
+			expectedHost:         "backend",
+			expectedRoutingKey:   "backend",
+			expectedTimeout:      2 * time.Second,
+			expectedListenerType: "tcp",
+			expectedOutput:       "json",
+		},
+		{
+			name:                 "test2",
+			flags:                common.CommandListenerCreateFlags{"", "", "secret", "tcp", 3 * time.Second, "yaml"},
+			expectedTlsSecret:    "secret",
+			expectedHost:         "test2",
+			expectedRoutingKey:   "test2",
+			expectedTimeout:      3 * time.Second,
+			expectedListenerType: "tcp",
+			expectedOutput:       "yaml",
+		},
+	}
+
+	for _, test := range testTable {
+		t.Run(test.name, func(t *testing.T) {
+
+			cmd, err := newCmdListenerCreateWithMocks("test", nil, nil, "")
+			assert.Assert(t, err)
+
+			cmd.Flags = &test.flags
+			cmd.name = test.name
+
+			cmd.InputToOptions()
+
+			assert.Check(t, cmd.routingKey == test.expectedRoutingKey)
+			assert.Check(t, cmd.output == test.expectedOutput)
+			assert.Check(t, cmd.tlsSecret == test.expectedTlsSecret)
+			assert.Check(t, cmd.host == test.expectedHost)
+			assert.Check(t, cmd.timeout == test.expectedTimeout)
+			assert.Check(t, cmd.output == test.expectedOutput)
+			assert.Check(t, cmd.listenerType == test.expectedListenerType)
+		})
+	}
+}
+
 func TestCmdListenerCreate_Run(t *testing.T) {
 	type test struct {
 		name                string
@@ -730,11 +790,8 @@ func TestCmdListenerCreate_WaitUntil(t *testing.T) {
 
 		cmd.name = "my-listener"
 		cmd.port = 8080
-		cmd.Flags = &common.CommandListenerCreateFlags{
-			Timeout: 1 * time.Second,
-			Output:  test.output,
-		}
-		cmd.output = cmd.Flags.Output
+		cmd.output = test.output
+		cmd.timeout = 1 * time.Second
 		cmd.namespace = "test"
 
 		t.Run(test.name, func(t *testing.T) {
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),
 		},