Repository: skupperproject/skupper
Pull request #1846 (2024-12-13 23:11:15)

Add generate option to commands 
Add generate option to site/listener/connector commands for kube and nonkube
Remove output flag from same commands.

nluaces: I have found an error testing skupper site generate:

```
 $ skupper site generate west --enable-link-access
 
Warning: --bind-host flag is not supported on this platform
apiVersion: skupper.io/v2alpha1
kind: Site
metadata:
  name: west
  namespace: default
spec:
  linkAccess: default
```

This warning should not be printed.
nluaces: sorry, my mistake
lynnemorrison: 
> I have found an error testing skupper site generate:
> 
> ```
>  $ skupper site generate west --enable-link-access
>  
> Warning: --bind-host flag is not supported on this platform
> apiVersion: skupper.io/v2alpha1
> kind: Site
> metadata:
>   name: west
>   namespace: default
> spec:
>   linkAccess: default
> ```
> 
> This warning should not be printed.

looks like the warning has been removed from the site create, but was left in generate code.    You can't set binding-host on nonkube platform anyway.   I'm wondering if we should also remove:

if cmd.Flags != nil && cmd.Flags.SubjectAlternativeNames != nil && len(cmd.Flags.SubjectAlternativeNames) > 0 {
		fmt.Println("Warning: --subject-alternative-names flag is not supported on this platform")
	}

nluaces: > looks like the warning has been removed from the site create, but was left in generate code. You can't set binding-host on nonkube platform anyway

Originally, the warning was made to let the user know that flag was not meant for non-kube commands, and by default had an empty value.

Later, there was another change that put a default value in that flag.

Before doing the release, I had to remove the warning from site create because that warning was always printed in non-kube environments.

I think you can do the same in this case, removing the warning, otherwise is going to be printed. 

> @nluaces I'm wondering if we should also remove:
>if cmd.Flags != nil && cmd.Flags.SubjectAlternativeNames != nil && >len(cmd.Flags.SubjectAlternativeNames) > 0 {
>fmt.Println("Warning: --subject-alternative-names flag is not supported on this platform")
>}

It has to be removed if there is a value by default in that flag, otherwise I think it can remain, but I don't have a strong opinion about this, so if you think that is better to remove it as well I'm fine with it.

nluaces: tested:
- Hello world example (ok)
- generate commands in a running kubernetes environments (ok)
- generate commands without a kubernetes environment (needs to be fixed)

Note: the link generate command needs to have a kubernetes environment given that its behavior is different: it creates a credential resource and a secret in kubernetes in order to be able to generate a link resource. 
nluaces: @lynnemorrison there are some merge conflicts, I will merge this pull request after their resolution
nluaces: there are still some merge conflicts to solve

Patch:
diff --git a/internal/cmd/skupper/common/flags.go b/internal/cmd/skupper/common/flags.go
index 13cc0eaa..19d922de 100644
--- a/internal/cmd/skupper/common/flags.go
+++ b/internal/cmd/skupper/common/flags.go
@@ -57,7 +57,7 @@ for other Kubernetes flavors, loadbalancer is the default.`
 	FlagNameSelector            = "selector"
 	FlagDescSelector            = "A Kubernetes label selector for specifying target server pods."
 	FlagNameWorkload            = "workload"
-	FlagDescWorkload            = "A Kubernetes resource name that identifies a workload expressed like resource-type/resource-name. Expected resource types: services, daemonsets, deployments, and statefulsets."
+	FlagDescWorkload            = "A Kubernetes resource name that identifies a workload expressed like resource-type/resource-name. Expected resource types: service, daemonset, deployment, and statefulset."
 
 	FlagNameConnectorPort = "port"
 	FlagDescConnectorPort = "The port of the local connector"
@@ -92,8 +92,6 @@ type CommandSiteCreateFlags struct {
 	EnableLinkAccess        bool
 	LinkAccessType          string
 	ServiceAccount          string
-	Output                  string
-	Host                    string
 	Timeout                 time.Duration
 	BindHost                string
 	SubjectAlternativeNames []string
@@ -104,8 +102,6 @@ type CommandSiteUpdateFlags struct {
 	EnableLinkAccess        bool
 	LinkAccessType          string
 	ServiceAccount          string
-	Output                  string
-	Host                    string
 	Timeout                 time.Duration
 	BindHost                string
 	SubjectAlternativeNames []string
@@ -122,6 +118,15 @@ type CommandSiteStatusFlags struct {
 	Output string
 }
 
+type CommandSiteGenerateFlags struct {
+	EnableLinkAccess        bool
+	LinkAccessType          string
+	ServiceAccount          string
+	BindHost                string
+	SubjectAlternativeNames []string
+	Output                  string
+}
+
 type CommandLinkGenerateFlags struct {
 	TlsCredentials     string
 	Cost               string
@@ -132,7 +137,6 @@ type CommandLinkGenerateFlags struct {
 type CommandLinkUpdateFlags struct {
 	TlsCredentials string
 	Cost           string
-	Output         string
 	Timeout        time.Duration
 	Wait           string
 }
@@ -167,7 +171,6 @@ type CommandConnectorCreateFlags struct {
 	IncludeNotReadyPods bool
 	Workload            string
 	Timeout             time.Duration
-	Output              string
 	Wait                string
 }
 
@@ -181,7 +184,6 @@ type CommandConnectorUpdateFlags struct {
 	Selector            string
 	IncludeNotReadyPods bool
 	Timeout             time.Duration
-	Output              string
 	Wait                string
 }
 
@@ -194,13 +196,23 @@ type CommandConnectorStatusFlags struct {
 	Output string
 }
 
+type CommandConnectorGenerateFlags struct {
+	RoutingKey          string
+	Host                string
+	Selector            string
+	TlsCredentials      string
+	ConnectorType       string
+	IncludeNotReadyPods bool
+	Workload            string
+	Output              string
+}
+
 type CommandListenerCreateFlags struct {
 	RoutingKey     string
 	Host           string
 	TlsCredentials string
 	ListenerType   string
 	Timeout        time.Duration
-	Output         string
 	Wait           string
 }
 
@@ -211,7 +223,6 @@ type CommandListenerUpdateFlags struct {
 	ListenerType   string
 	Timeout        time.Duration
 	Port           int
-	Output         string
 	Wait           string
 }
 
@@ -230,6 +241,14 @@ type CommandSystemSetupFlags struct {
 	Force    bool
 }
 
+type CommandListenerGenerateFlags struct {
+	RoutingKey     string
+	Host           string
+	TlsCredentials string
+	ListenerType   string
+	Output         string
+}
+
 type CommandVersionFlags struct {
 	Output string
 }
diff --git a/internal/cmd/skupper/connector/connector.go b/internal/cmd/skupper/connector/connector.go
index 4648a566..4171bb82 100644
--- a/internal/cmd/skupper/connector/connector.go
+++ b/internal/cmd/skupper/connector/connector.go
@@ -25,6 +25,7 @@ skupper connector status my-connector`,
 	cmd.AddCommand(CmdConnectorStatusFactory(config.GetPlatform()))
 	cmd.AddCommand(CmdConnectorUpdateFactory(config.GetPlatform()))
 	cmd.AddCommand(CmdConnectorDeleteFactory(config.GetPlatform()))
+	cmd.AddCommand(CmdConnectorGenerateFactory(config.GetPlatform()))
 
 	return cmd
 }
@@ -49,7 +50,6 @@ skupper connector create backend 8080 --workload deployment/backend`,
 	cmd.Flags().StringVar(&cmdFlags.Host, common.FlagNameHost, "", common.FlagDescHost)
 	cmd.Flags().StringVar(&cmdFlags.TlsCredentials, common.FlagNameTlsCredentials, "", common.FlagDescTlsCredentials)
 	cmd.Flags().StringVar(&cmdFlags.ConnectorType, common.FlagNameConnectorType, "tcp", common.FlagDescConnectorType)
-	cmd.Flags().StringVarP(&cmdFlags.Output, common.FlagNameOutput, "o", "", common.FlagDescOutput)
 	if configuredPlatform == types.PlatformKubernetes {
 		cmd.Flags().BoolVarP(&cmdFlags.IncludeNotReadyPods, common.FlagNameIncludeNotReadyPods, "i", false, common.FlagDescIncludeNotRead)
 		cmd.Flags().StringVarP(&cmdFlags.Selector, common.FlagNameSelector, "s", "", common.FlagDescSelector)
@@ -110,7 +110,6 @@ func CmdConnectorUpdateFactory(configuredPlatform types.Platform) *cobra.Command
 	cmd.Flags().StringVar(&cmdFlags.Host, common.FlagNameHost, "", common.FlagDescHost)
 	cmd.Flags().StringVar(&cmdFlags.TlsCredentials, common.FlagNameTlsCredentials, "", common.FlagDescTlsCredentials)
 	cmd.Flags().StringVar(&cmdFlags.ConnectorType, common.FlagNameConnectorType, "tcp", common.FlagDescConnectorType)
-	cmd.Flags().StringVarP(&cmdFlags.Output, common.FlagNameOutput, "o", "", common.FlagDescOutput)
 	cmd.Flags().IntVar(&cmdFlags.Port, common.FlagNameConnectorPort, 0, common.FlagDescConnectorPort)
 	if configuredPlatform == types.PlatformKubernetes {
 		cmd.Flags().BoolVarP(&cmdFlags.IncludeNotReadyPods, common.FlagNameIncludeNotReadyPods, "i", false, common.FlagDescIncludeNotRead)
@@ -152,3 +151,38 @@ func CmdConnectorDeleteFactory(configuredPlatform types.Platform) *cobra.Command
 
 	return cmd
 }
+
+func CmdConnectorGenerateFactory(configuredPlatform types.Platform) *cobra.Command {
+	kubeCommand := kube.NewCmdConnectorGenerate()
+	nonKubeCommand := nonkube.NewCmdConnectorGenerate()
+
+	cmdConnectorGenerateDesc := common.SkupperCmdDescription{
+		Use:   "generate <name> <port>",
+		Short: "Generate a connector resource and output it to a file or screen",
+		Long:  "Clients at this site use the connector host and port to establish connections to the remote service.",
+		Example: `skupper connector generate database 5432
+skupper connector generate backend 8080 --workload deployment/backend`,
+	}
+
+	cmd := common.ConfigureCobraCommand(configuredPlatform, cmdConnectorGenerateDesc, kubeCommand, nonKubeCommand)
+
+	cmdFlags := common.CommandConnectorGenerateFlags{}
+
+	cmd.Flags().StringVarP(&cmdFlags.RoutingKey, common.FlagNameRoutingKey, "r", "", common.FlagDescRoutingKey)
+	cmd.Flags().StringVar(&cmdFlags.Host, common.FlagNameHost, "", common.FlagDescHost)
+	cmd.Flags().StringVar(&cmdFlags.TlsCredentials, common.FlagNameTlsCredentials, "", common.FlagDescTlsCredentials)
+	cmd.Flags().StringVar(&cmdFlags.ConnectorType, common.FlagNameConnectorType, "tcp", common.FlagDescConnectorType)
+	cmd.Flags().StringVarP(&cmdFlags.Output, common.FlagNameOutput, "o", "yaml", common.FlagDescOutput)
+	if configuredPlatform == types.PlatformKubernetes {
+		cmd.Flags().BoolVarP(&cmdFlags.IncludeNotReadyPods, common.FlagNameIncludeNotReadyPods, "i", false, common.FlagDescIncludeNotRead)
+		cmd.Flags().StringVarP(&cmdFlags.Selector, common.FlagNameSelector, "s", "", common.FlagDescSelector)
+		cmd.Flags().StringVarP(&cmdFlags.Workload, common.FlagNameWorkload, "w", "", common.FlagDescWorkload)
+	}
+
+	kubeCommand.CobraCmd = cmd
+	kubeCommand.Flags = &cmdFlags
+	nonKubeCommand.CobraCmd = cmd
+	nonKubeCommand.Flags = &cmdFlags
+
+	return cmd
+}
diff --git a/internal/cmd/skupper/connector/kube/connector_create.go b/internal/cmd/skupper/connector/kube/connector_create.go
index 60eb2488..57cacc55 100644
--- a/internal/cmd/skupper/connector/kube/connector_create.go
+++ b/internal/cmd/skupper/connector/kube/connector_create.go
@@ -4,10 +4,12 @@ import (
 	"context"
 	"errors"
 	"fmt"
-	"k8s.io/apimachinery/pkg/api/meta"
+	"net"
 	"strconv"
 	"time"
 
+	"k8s.io/apimachinery/pkg/api/meta"
+
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 
@@ -29,7 +31,6 @@ type CmdConnectorCreate struct {
 	namespace           string
 	name                string
 	port                int
-	output              string
 	host                string
 	selector            string
 	tlsCredentials      string
@@ -60,11 +61,11 @@ func (cmd *CmdConnectorCreate) ValidateInput(args []string) error {
 	resourceStringValidator := validator.NewResourceStringValidator()
 	numberValidator := validator.NewNumberValidator()
 	connectorTypeValidator := validator.NewOptionValidator(common.ConnectorTypes)
-	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
 	timeoutValidator := validator.NewTimeoutInSecondsValidator()
 	workloadStringValidator := validator.NewWorkloadStringValidator(common.WorkloadTypes)
 	selectorStringValidator := validator.NewSelectorStringValidator()
 	statusValidator := validator.NewOptionValidator(common.WaitStatusTypes)
+	hostStringValidator := validator.NewHostStringValidator()
 
 	// Validate arguments name and port
 	if len(args) < 2 {
@@ -125,7 +126,11 @@ func (cmd *CmdConnectorCreate) ValidateInput(args []string) error {
 		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
+		ip := net.ParseIP(cmd.Flags.Host)
+		ok, _ := hostStringValidator.Evaluate(cmd.Flags.Host)
+		if !ok && ip == nil {
+			validationErrors = append(validationErrors, fmt.Errorf("host is not valid: a valid IP address or hostname is expected"))
+		}
 	}
 	if cmd.Flags != nil && cmd.Flags.Selector != "" {
 		if cmd.Flags.Workload != "" || cmd.Flags.Host != "" {
@@ -194,19 +199,12 @@ func (cmd *CmdConnectorCreate) ValidateInput(args []string) error {
 			}
 		}
 	}
-	//TBD what is valid timeout
 	if cmd.Flags != nil && cmd.Flags.Timeout.String() != "" {
 		ok, err := timeoutValidator.Evaluate(cmd.Flags.Timeout)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid: %s", err))
 		}
 	}
-	if cmd.Flags != nil && cmd.Flags.Output != "" {
-		ok, err := outputTypeValidator.Evaluate(cmd.Flags.Output)
-		if !ok {
-			validationErrors = append(validationErrors, fmt.Errorf("output type is not valid: %s", err))
-		}
-	}
 
 	if cmd.Flags != nil && cmd.Flags.Wait != "" {
 		ok, err := statusValidator.Evaluate(cmd.Flags.Wait)
@@ -241,7 +239,6 @@ func (cmd *CmdConnectorCreate) InputToOptions() {
 	cmd.timeout = cmd.Flags.Timeout
 	cmd.tlsCredentials = cmd.Flags.TlsCredentials
 	cmd.connectorType = cmd.Flags.ConnectorType
-	cmd.output = cmd.Flags.Output
 	cmd.includeNotReadyPods = cmd.Flags.IncludeNotReadyPods
 	cmd.status = cmd.Flags.Wait
 }
@@ -268,21 +265,11 @@ func (cmd *CmdConnectorCreate) Run() error {
 		},
 	}
 
-	if cmd.output != "" {
-		encodedOutput, err := utils.Encode(cmd.output, resource)
-		fmt.Println(encodedOutput)
-		return err
-	} else {
-		_, err := cmd.client.Connectors(cmd.namespace).Create(context.TODO(), &resource, metav1.CreateOptions{})
-		return err
-	}
+	_, err := cmd.client.Connectors(cmd.namespace).Create(context.TODO(), &resource, metav1.CreateOptions{})
+	return err
 }
 
 func (cmd *CmdConnectorCreate) WaitUntil() error {
-	// the site resource was not created
-	if cmd.output != "" {
-		return nil
-	}
 
 	if cmd.status == "none" {
 		return nil
diff --git a/internal/cmd/skupper/connector/kube/connector_generate.go b/internal/cmd/skupper/connector/kube/connector_generate.go
new file mode 100644
index 00000000..4f18fd07
--- /dev/null
+++ b/internal/cmd/skupper/connector/kube/connector_generate.go
@@ -0,0 +1,191 @@
+package kube
+
+import (
+	"errors"
+	"fmt"
+	"net"
+	"strconv"
+
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
+
+	"github.com/skupperproject/skupper/internal/utils/validator"
+	"github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
+	skupperv2alpha1 "github.com/skupperproject/skupper/pkg/generated/client/clientset/versioned/typed/skupper/v2alpha1"
+	"github.com/spf13/cobra"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+type CmdConnectorGenerate struct {
+	client              skupperv2alpha1.SkupperV2alpha1Interface
+	CobraCmd            *cobra.Command
+	Flags               *common.CommandConnectorGenerateFlags
+	namespace           string
+	name                string
+	port                int
+	output              string
+	host                string
+	selector            string
+	tlsCredentials      string
+	routingKey          string
+	connectorType       string
+	includeNotReadyPods bool
+}
+
+func NewCmdConnectorGenerate() *CmdConnectorGenerate {
+
+	return &CmdConnectorGenerate{}
+}
+
+func (cmd *CmdConnectorGenerate) NewClient(cobraCommand *cobra.Command, args []string) {
+
+	cmd.namespace = cobraCommand.Flag("namespace").Value.String()
+}
+
+func (cmd *CmdConnectorGenerate) ValidateInput(args []string) error {
+	var validationErrors []error
+	resourceStringValidator := validator.NewResourceStringValidator()
+	numberValidator := validator.NewNumberValidator()
+	connectorTypeValidator := validator.NewOptionValidator(common.ConnectorTypes)
+	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
+	workloadStringValidator := validator.NewWorkloadStringValidator(common.WorkloadTypes)
+	selectorStringValidator := validator.NewSelectorStringValidator()
+	hostStringValidator := validator.NewHostStringValidator()
+
+	// Validate arguments name and port
+	if len(args) < 2 {
+		validationErrors = append(validationErrors, fmt.Errorf("connector name and port must be configured"))
+	} else if len(args) > 2 {
+		validationErrors = append(validationErrors, fmt.Errorf("only two arguments are allowed for this command"))
+	} else if args[0] == "" {
+		validationErrors = append(validationErrors, fmt.Errorf("connector name must not be empty"))
+	} else if args[1] == "" {
+		validationErrors = append(validationErrors, fmt.Errorf("connector port must not be empty"))
+	} else {
+		ok, err := resourceStringValidator.Evaluate(args[0])
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("connector name is not valid: %s", err))
+		} else {
+			cmd.name = args[0]
+		}
+
+		cmd.port, err = strconv.Atoi(args[1])
+		if err != nil {
+			validationErrors = append(validationErrors, fmt.Errorf("connector port is not valid: %s", err))
+		}
+		ok, err = numberValidator.Evaluate(cmd.port)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("connector port is not valid: %s", err))
+		}
+	}
+
+	// Validate flags
+	if cmd.Flags != nil && cmd.Flags.RoutingKey != "" {
+		ok, err := resourceStringValidator.Evaluate(cmd.Flags.RoutingKey)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("routing key is not valid: %s", err))
+		}
+	}
+	if cmd.Flags != nil && cmd.Flags.TlsCredentials != "" {
+		ok, err := resourceStringValidator.Evaluate(cmd.Flags.TlsCredentials)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("tlsCredentials is not valid: %s", err))
+		}
+	}
+	if cmd.Flags != nil && cmd.Flags.ConnectorType != "" {
+		ok, err := connectorTypeValidator.Evaluate(cmd.Flags.ConnectorType)
+		if !ok {
+			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 != nil && cmd.Flags.Host != "" {
+		if cmd.Flags.Workload != "" || cmd.Flags.Selector != "" {
+			validationErrors = append(validationErrors, fmt.Errorf("If host is configured, cannot configure workload or selector"))
+		}
+		ip := net.ParseIP(cmd.Flags.Host)
+		ok, _ := hostStringValidator.Evaluate(cmd.Flags.Host)
+		if !ok && ip == nil {
+			validationErrors = append(validationErrors, fmt.Errorf("host is not valid: a valid IP address or hostname is expected"))
+		}
+	}
+	if cmd.Flags != nil && cmd.Flags.Selector != "" {
+		if cmd.Flags.Workload != "" || cmd.Flags.Host != "" {
+			validationErrors = append(validationErrors, fmt.Errorf("If selector is configured, cannot configure workload or host"))
+		}
+		ok, err := selectorStringValidator.Evaluate(cmd.Flags.Selector)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("selector is not valid: %s", err))
+		}
+		cmd.selector = cmd.Flags.Selector
+	}
+	if cmd.Flags != nil && cmd.Flags.Workload != "" {
+		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))
+		}
+	}
+	if cmd.Flags != nil && cmd.Flags.Output != "" {
+		ok, err := outputTypeValidator.Evaluate(cmd.Flags.Output)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("output type is not valid: %s", err))
+		}
+	}
+	return errors.Join(validationErrors...)
+}
+
+func (cmd *CmdConnectorGenerate) 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.tlsCredentials = cmd.Flags.TlsCredentials
+	cmd.connectorType = cmd.Flags.ConnectorType
+	cmd.output = cmd.Flags.Output
+	cmd.includeNotReadyPods = cmd.Flags.IncludeNotReadyPods
+}
+
+func (cmd *CmdConnectorGenerate) Run() error {
+	resource := v2alpha1.Connector{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "Connector",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      cmd.name,
+			Namespace: cmd.namespace,
+		},
+		Spec: v2alpha1.ConnectorSpec{
+			Host:                cmd.host,
+			Port:                cmd.port,
+			RoutingKey:          cmd.routingKey,
+			TlsCredentials:      cmd.tlsCredentials,
+			Type:                cmd.connectorType,
+			IncludeNotReadyPods: cmd.includeNotReadyPods,
+			Selector:            cmd.selector,
+		},
+	}
+
+	encodedOutput, err := utils.Encode(cmd.output, resource)
+	fmt.Println(encodedOutput)
+	return err
+}
+
+func (cmd *CmdConnectorGenerate) WaitUntil() error { return nil }
diff --git a/internal/cmd/skupper/connector/kube/connector_update.go b/internal/cmd/skupper/connector/kube/connector_update.go
index c84ba81a..caaba0cb 100644
--- a/internal/cmd/skupper/connector/kube/connector_update.go
+++ b/internal/cmd/skupper/connector/kube/connector_update.go
@@ -4,9 +4,10 @@ import (
 	"context"
 	"errors"
 	"fmt"
-	"k8s.io/apimachinery/pkg/api/meta"
 	"time"
 
+	"k8s.io/apimachinery/pkg/api/meta"
+
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 
@@ -31,7 +32,6 @@ type ConnectorUpdates struct {
 	selector            string
 	includeNotReadyPods bool
 	timeout             time.Duration
-	output              string
 }
 
 type CmdConnectorUpdate struct {
@@ -68,7 +68,6 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) error {
 	resourceStringValidator := validator.NewResourceStringValidator()
 	numberValidator := validator.NewNumberValidator()
 	connectorTypeValidator := validator.NewOptionValidator(common.ConnectorTypes)
-	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
 	timeoutValidator := validator.NewTimeoutInSecondsValidator()
 	workloadStringValidator := validator.NewWorkloadStringValidator(common.WorkloadTypes)
 	selectorStringValidator := validator.NewSelectorStringValidator()
@@ -231,14 +230,6 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) error {
 			cmd.newSettings.selector = cmd.newSettings.workload
 		}
 	}
-	if cmd.Flags != nil && 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.newSettings.output = cmd.Flags.Output
-		}
-	}
 
 	if cmd.Flags != nil && cmd.Flags.Wait != "" {
 		ok, err := statusValidator.Evaluate(cmd.Flags.Wait)
@@ -273,23 +264,12 @@ func (cmd *CmdConnectorUpdate) Run() error {
 		},
 	}
 
-	if cmd.newSettings.output != "" {
-		encodedOutput, err := utils.Encode(cmd.newSettings.output, resource)
-		fmt.Println(encodedOutput)
-		return err
-	} else {
-		_, err := cmd.client.Connectors(cmd.namespace).Update(context.TODO(), &resource, metav1.UpdateOptions{})
-		return err
-	}
+	_, err := cmd.client.Connectors(cmd.namespace).Update(context.TODO(), &resource, metav1.UpdateOptions{})
+	return err
 }
 
 func (cmd *CmdConnectorUpdate) WaitUntil() error {
 
-	// the site resource was not created
-	if cmd.newSettings.output != "" {
-		return nil
-	}
-
 	if cmd.status == "none" {
 		return nil
 	}
diff --git a/internal/cmd/skupper/connector/nonkube/connector_create.go b/internal/cmd/skupper/connector/nonkube/connector_create.go
index 41c8e361..f8e607c0 100644
--- a/internal/cmd/skupper/connector/nonkube/connector_create.go
+++ b/internal/cmd/skupper/connector/nonkube/connector_create.go
@@ -7,7 +7,6 @@ import (
 	"strconv"
 
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"github.com/skupperproject/skupper/internal/nonkube/client/fs"
 	"github.com/skupperproject/skupper/internal/utils/validator"
 	"github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
@@ -22,7 +21,6 @@ type CmdConnectorCreate struct {
 	namespace        string
 	connectorName    string
 	port             int
-	output           string
 	host             string
 	routingKey       string
 	connectorType    string
@@ -55,7 +53,6 @@ func (cmd *CmdConnectorCreate) ValidateInput(args []string) error {
 	resourceStringValidator := validator.NewResourceStringValidator()
 	numberValidator := validator.NewNumberValidator()
 	connectorTypeValidator := validator.NewOptionValidator(common.ConnectorTypes)
-	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
 	hostStringValidator := validator.NewHostStringValidator()
 
 	// Validate arguments name and port
@@ -97,12 +94,6 @@ func (cmd *CmdConnectorCreate) ValidateInput(args []string) error {
 			validationErrors = append(validationErrors, fmt.Errorf("connector type is not valid: %s", err))
 		}
 	}
-	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))
-		}
-	}
 	if cmd.Flags.Host != "" {
 		ip := net.ParseIP(cmd.Flags.Host)
 		ok, _ := hostStringValidator.Evaluate(cmd.Flags.Host)
@@ -136,7 +127,6 @@ func (cmd *CmdConnectorCreate) InputToOptions() {
 
 	cmd.host = cmd.Flags.Host
 	cmd.connectorType = cmd.Flags.ConnectorType
-	cmd.output = cmd.Flags.Output
 	cmd.tlsCredentials = cmd.Flags.TlsCredentials
 }
 
@@ -159,15 +149,9 @@ func (cmd *CmdConnectorCreate) Run() error {
 		},
 	}
 
-	if cmd.output != "" {
-		encodedOutput, err := utils.Encode(cmd.output, connectorResource)
-		fmt.Println(encodedOutput)
+	err := cmd.connectorHandler.Add(connectorResource)
+	if err != nil {
 		return err
-	} else {
-		err := cmd.connectorHandler.Add(connectorResource)
-		if err != nil {
-			return err
-		}
 	}
 	return nil
 }
diff --git a/internal/cmd/skupper/connector/nonkube/connector_generate.go b/internal/cmd/skupper/connector/nonkube/connector_generate.go
new file mode 100644
index 00000000..78758575
--- /dev/null
+++ b/internal/cmd/skupper/connector/nonkube/connector_generate.go
@@ -0,0 +1,168 @@
+package nonkube
+
+import (
+	"errors"
+	"fmt"
+	"net"
+	"strconv"
+
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
+	"github.com/skupperproject/skupper/internal/nonkube/client/fs"
+	"github.com/skupperproject/skupper/internal/utils/validator"
+	"github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
+	"github.com/spf13/cobra"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+type CmdConnectorGenerate struct {
+	connectorHandler *fs.ConnectorHandler
+	CobraCmd         *cobra.Command
+	Flags            *common.CommandConnectorGenerateFlags
+	namespace        string
+	connectorName    string
+	port             int
+	output           string
+	host             string
+	routingKey       string
+	connectorType    string
+	tlsCredentials   string
+}
+
+func NewCmdConnectorGenerate() *CmdConnectorGenerate {
+	return &CmdConnectorGenerate{}
+}
+
+func (cmd *CmdConnectorGenerate) NewClient(cobraCommand *cobra.Command, args []string) {
+	if cmd.CobraCmd != nil && cmd.CobraCmd.Flag(common.FlagNameNamespace) != nil && cmd.CobraCmd.Flag(common.FlagNameNamespace).Value.String() != "" {
+		cmd.namespace = cmd.CobraCmd.Flag(common.FlagNameNamespace).Value.String()
+	}
+
+	cmd.connectorHandler = fs.NewConnectorHandler(cmd.namespace)
+}
+
+func (cmd *CmdConnectorGenerate) ValidateInput(args []string) error {
+	var validationErrors []error
+
+	if cmd.CobraCmd != nil && cmd.CobraCmd.Flag(common.FlagNameContext) != nil && cmd.CobraCmd.Flag(common.FlagNameContext).Value.String() != "" {
+		fmt.Println("Warning: --context flag is not supported on this platform")
+	}
+
+	if cmd.CobraCmd != nil && cmd.CobraCmd.Flag(common.FlagNameKubeconfig) != nil && cmd.CobraCmd.Flag(common.FlagNameKubeconfig).Value.String() != "" {
+		fmt.Println("Warning: --kubeconfig flag is not supported on this platform")
+	}
+
+	resourceStringValidator := validator.NewResourceStringValidator()
+	numberValidator := validator.NewNumberValidator()
+	connectorTypeValidator := validator.NewOptionValidator(common.ConnectorTypes)
+	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
+	hostStringValidator := validator.NewHostStringValidator()
+
+	// Validate arguments name and port
+	if len(args) < 2 {
+		validationErrors = append(validationErrors, fmt.Errorf("connector name and port must be configured"))
+	} else if len(args) > 2 {
+		validationErrors = append(validationErrors, fmt.Errorf("only two arguments are allowed for this command"))
+	} else if args[0] == "" {
+		validationErrors = append(validationErrors, fmt.Errorf("connector name must not be empty"))
+	} else if args[1] == "" {
+		validationErrors = append(validationErrors, fmt.Errorf("connector port must not be empty"))
+	} else {
+		ok, err := resourceStringValidator.Evaluate(args[0])
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("connector name is not valid: %s", err))
+		} else {
+			cmd.connectorName = args[0]
+		}
+		cmd.port, err = strconv.Atoi(args[1])
+		if err != nil {
+			validationErrors = append(validationErrors, fmt.Errorf("connector port is not valid: %s", err))
+		}
+		ok, err = numberValidator.Evaluate(cmd.port)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("connector port is not valid: %s", err))
+		}
+	}
+
+	// Validate flags
+	if cmd.Flags.RoutingKey != "" {
+		ok, err := resourceStringValidator.Evaluate(cmd.Flags.RoutingKey)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("routing key is not valid: %s", err))
+		}
+	}
+	if cmd.Flags.ConnectorType != "" {
+		ok, err := connectorTypeValidator.Evaluate(cmd.Flags.ConnectorType)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("connector type is not valid: %s", err))
+		}
+	}
+	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))
+		}
+	}
+	if cmd.Flags.Host != "" {
+		ip := net.ParseIP(cmd.Flags.Host)
+		ok, _ := hostStringValidator.Evaluate(cmd.Flags.Host)
+		if !ok && ip == nil {
+			validationErrors = append(validationErrors, fmt.Errorf("host is not valid: a valid IP address or hostname is expected"))
+		}
+	} else {
+		validationErrors = append(validationErrors, fmt.Errorf("host name must be configured: an IP address or hostname is expected"))
+	}
+	if cmd.Flags.TlsCredentials != "" {
+		ok, err := resourceStringValidator.Evaluate(cmd.Flags.TlsCredentials)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("tlsCredentials is not valid: %s", err))
+		}
+	}
+
+	return errors.Join(validationErrors...)
+}
+
+func (cmd *CmdConnectorGenerate) InputToOptions() {
+	// default routingkey to name of connector
+	if cmd.Flags.RoutingKey == "" {
+		cmd.routingKey = cmd.connectorName
+	} else {
+		cmd.routingKey = cmd.Flags.RoutingKey
+	}
+
+	if cmd.namespace == "" {
+		cmd.namespace = "default"
+	}
+
+	cmd.host = cmd.Flags.Host
+	cmd.connectorType = cmd.Flags.ConnectorType
+	cmd.tlsCredentials = cmd.Flags.TlsCredentials
+	cmd.output = cmd.Flags.Output
+}
+
+func (cmd *CmdConnectorGenerate) Run() error {
+	connectorResource := v2alpha1.Connector{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "Connector",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      cmd.connectorName,
+			Namespace: cmd.namespace,
+		},
+		Spec: v2alpha1.ConnectorSpec{
+			Host:           cmd.host,
+			Port:           cmd.port,
+			RoutingKey:     cmd.routingKey,
+			TlsCredentials: cmd.tlsCredentials,
+			Type:           cmd.connectorType,
+		},
+	}
+
+	encodedOutput, err := utils.Encode(cmd.output, connectorResource)
+	fmt.Println(encodedOutput)
+	return err
+
+}
+
+func (cmd *CmdConnectorGenerate) WaitUntil() error { return nil }
diff --git a/internal/cmd/skupper/connector/nonkube/connector_update.go b/internal/cmd/skupper/connector/nonkube/connector_update.go
index 7476571c..4cd30ba7 100644
--- a/internal/cmd/skupper/connector/nonkube/connector_update.go
+++ b/internal/cmd/skupper/connector/nonkube/connector_update.go
@@ -6,7 +6,6 @@ import (
 	"net"
 
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"github.com/skupperproject/skupper/internal/nonkube/client/fs"
 	"github.com/skupperproject/skupper/internal/utils/validator"
 	"github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
@@ -28,7 +27,6 @@ type CmdConnectorUpdate struct {
 	namespace        string
 	connectorName    string
 	newSettings      ConnectorUpdates
-	output           string
 }
 
 func NewCmdConnectorUpdate() *CmdConnectorUpdate {
@@ -49,7 +47,6 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) error {
 	resourceStringValidator := validator.NewResourceStringValidator()
 	numberValidator := validator.NewNumberValidator()
 	connectorTypeValidator := validator.NewOptionValidator(common.ConnectorTypes)
-	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
 	hostStringValidator := validator.NewHostStringValidator()
 
 	if cmd.CobraCmd != nil && cmd.CobraCmd.Flag(common.FlagNameContext) != nil && cmd.CobraCmd.Flag(common.FlagNameContext).Value.String() != "" {
@@ -125,14 +122,6 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) error {
 			cmd.newSettings.host = cmd.Flags.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
-		}
-	}
 	if cmd.Flags.TlsCredentials != "" {
 		ok, err := resourceStringValidator.Evaluate(cmd.Flags.TlsCredentials)
 		if !ok {
@@ -169,17 +158,9 @@ func (cmd *CmdConnectorUpdate) Run() error {
 		},
 	}
 
-	if cmd.output != "" {
-		encodedOutput, err := utils.Encode(cmd.output, connectorResource)
-		fmt.Println(encodedOutput)
-		return err
-	} else {
-		err := cmd.connectorHandler.Add(connectorResource)
-		if err != nil {
-			return err
-		}
-	}
-	return nil
+	err := cmd.connectorHandler.Add(connectorResource)
+	return err
+
 }
 
 func (cmd *CmdConnectorUpdate) WaitUntil() error { return nil }
diff --git a/internal/cmd/skupper/link/kube/link_update.go b/internal/cmd/skupper/link/kube/link_update.go
index 47422653..d2f468ec 100644
--- a/internal/cmd/skupper/link/kube/link_update.go
+++ b/internal/cmd/skupper/link/kube/link_update.go
@@ -31,7 +31,6 @@ type CmdLinkUpdate struct {
 	Namespace      string
 	tlsCredentials string
 	cost           int
-	output         string
 	timeout        time.Duration
 	status         string
 }
@@ -54,7 +53,6 @@ func (cmd *CmdLinkUpdate) ValidateInput(args []string) error {
 	var validationErrors []error
 	numberValidator := validator.NewNumberValidator()
 	timeoutValidator := validator.NewTimeoutInSecondsValidator()
-	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
 	statusValidator := validator.NewOptionValidator(common.WaitStatusTypes)
 
 	//Validate if there is already a site defined in the namespace
@@ -91,13 +89,6 @@ func (cmd *CmdLinkUpdate) ValidateInput(args []string) error {
 		validationErrors = append(validationErrors, fmt.Errorf("link cost is not valid: %s", err))
 	}
 
-	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))
-		}
-	}
-
 	ok, err = timeoutValidator.Evaluate(cmd.Flags.Timeout)
 	if !ok {
 		validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid: %s", err))
@@ -117,7 +108,6 @@ func (cmd *CmdLinkUpdate) InputToOptions() {
 
 	cmd.cost, _ = strconv.Atoi(cmd.Flags.Cost)
 	cmd.tlsCredentials = cmd.Flags.TlsCredentials
-	cmd.output = cmd.Flags.Output
 	cmd.timeout = cmd.Flags.Timeout
 	cmd.status = cmd.Flags.Wait
 
@@ -158,26 +148,13 @@ func (cmd *CmdLinkUpdate) Run() error {
 		},
 	}
 
-	if cmd.output != "" {
-		encodedOutput, err := utils.Encode(cmd.output, resource)
-		fmt.Println(encodedOutput)
-
-		return err
-
-	} else {
-		_, err := cmd.Client.Links(cmd.Namespace).Update(context.TODO(), &resource, metav1.UpdateOptions{})
-		return err
-	}
+	_, err = cmd.Client.Links(cmd.Namespace).Update(context.TODO(), &resource, metav1.UpdateOptions{})
+	return err
 
 }
 
 func (cmd *CmdLinkUpdate) WaitUntil() error {
 
-	// the site resource was not created
-	if cmd.output != "" {
-		return nil
-	}
-
 	if cmd.status == "none" {
 		return nil
 	}
diff --git a/internal/cmd/skupper/link/link.go b/internal/cmd/skupper/link/link.go
index aeb75b19..1448644d 100644
--- a/internal/cmd/skupper/link/link.go
+++ b/internal/cmd/skupper/link/link.go
@@ -71,7 +71,6 @@ func CmdLinkUpdateFactory(configuredPlatform types.Platform) *cobra.Command {
 	cmd.Flags().StringVar(&cmdFlags.TlsCredentials, common.FlagNameTlsCredentials, "", common.FlagDescTlsCredentials)
 	cmd.Flags().StringVar(&cmdFlags.Cost, common.FlagNameCost, "1", common.FlagDescCost)
 	cmd.Flags().DurationVar(&cmdFlags.Timeout, common.FlagNameTimeout, 60*time.Second, common.FlagDescTimeout)
-	cmd.Flags().StringVarP(&cmdFlags.Output, common.FlagNameOutput, "o", "", common.FlagDescOutput)
 	if configuredPlatform == types.PlatformKubernetes {
 		cmd.Flags().StringVar(&cmdFlags.Wait, common.FlagNameWait, "ready", common.FlagDescWait)
 	}
diff --git a/internal/cmd/skupper/listener/kube/listener_create.go b/internal/cmd/skupper/listener/kube/listener_create.go
index 75a9b5b6..fd65742c 100644
--- a/internal/cmd/skupper/listener/kube/listener_create.go
+++ b/internal/cmd/skupper/listener/kube/listener_create.go
@@ -34,7 +34,6 @@ type CmdListenerCreate struct {
 	listenerType   string
 	routingKey     string
 	timeout        time.Duration
-	output         string
 	KubeClient     kubernetes.Interface
 	status         string
 }
@@ -59,7 +58,6 @@ func (cmd *CmdListenerCreate) ValidateInput(args []string) error {
 	resourceStringValidator := validator.NewResourceStringValidator()
 	numberValidator := validator.NewNumberValidator()
 	listenerTypeValidator := validator.NewOptionValidator(common.ListenerTypes)
-	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
 	timeoutValidator := validator.NewTimeoutInSecondsValidator()
 	statusValidator := validator.NewOptionValidator(common.WaitStatusTypes)
 
@@ -128,13 +126,6 @@ func (cmd *CmdListenerCreate) ValidateInput(args []string) error {
 		}
 	}
 
-	if cmd.Flags != nil && cmd.Flags.Output != "" {
-		ok, err := outputTypeValidator.Evaluate(cmd.Flags.Output)
-		if !ok {
-			validationErrors = append(validationErrors, fmt.Errorf("output type is not valid: %s", err))
-		}
-	}
-
 	if cmd.Flags != nil && cmd.Flags.Wait != "" {
 		ok, err := statusValidator.Evaluate(cmd.Flags.Wait)
 		if !ok {
@@ -160,7 +151,6 @@ func (cmd *CmdListenerCreate) InputToOptions() {
 	cmd.timeout = cmd.Flags.Timeout
 	cmd.tlsCredentials = cmd.Flags.TlsCredentials
 	cmd.listenerType = cmd.Flags.ListenerType
-	cmd.output = cmd.Flags.Output
 	cmd.status = cmd.Flags.Wait
 }
 
@@ -184,21 +174,11 @@ func (cmd *CmdListenerCreate) Run() error {
 		},
 	}
 
-	if cmd.output != "" {
-		encodedOutput, err := utils.Encode(cmd.output, resource)
-		fmt.Println(encodedOutput)
-		return err
-	} else {
-		_, err := cmd.client.Listeners(cmd.namespace).Create(context.TODO(), &resource, metav1.CreateOptions{})
-		return err
-	}
+	_, err := cmd.client.Listeners(cmd.namespace).Create(context.TODO(), &resource, metav1.CreateOptions{})
+	return err
 }
 
 func (cmd *CmdListenerCreate) WaitUntil() error {
-	// the site resource was not created
-	if cmd.output != "" {
-		return nil
-	}
 
 	if cmd.status == "none" {
 		return nil
diff --git a/internal/cmd/skupper/listener/kube/listener_generate.go b/internal/cmd/skupper/listener/kube/listener_generate.go
new file mode 100644
index 00000000..b4b714b6
--- /dev/null
+++ b/internal/cmd/skupper/listener/kube/listener_generate.go
@@ -0,0 +1,151 @@
+package kube
+
+import (
+	"errors"
+	"fmt"
+	"strconv"
+
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
+
+	"github.com/skupperproject/skupper/internal/utils/validator"
+	"github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
+	skupperv2alpha1 "github.com/skupperproject/skupper/pkg/generated/client/clientset/versioned/typed/skupper/v2alpha1"
+	"github.com/spf13/cobra"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+type CmdListenerGenerate struct {
+	client         skupperv2alpha1.SkupperV2alpha1Interface
+	CobraCmd       *cobra.Command
+	Flags          *common.CommandListenerGenerateFlags
+	namespace      string
+	name           string
+	port           int
+	host           string
+	tlsCredentials string
+	listenerType   string
+	routingKey     string
+	output         string
+}
+
+func NewCmdListenerGenerate() *CmdListenerGenerate {
+
+	return &CmdListenerGenerate{}
+
+}
+
+func (cmd *CmdListenerGenerate) NewClient(cobraCommand *cobra.Command, args []string) {
+
+	cmd.namespace = cobraCommand.Flag("namespace").Value.String()
+}
+
+func (cmd *CmdListenerGenerate) ValidateInput(args []string) error {
+	var validationErrors []error
+	resourceStringValidator := validator.NewResourceStringValidator()
+	numberValidator := validator.NewNumberValidator()
+	listenerTypeValidator := validator.NewOptionValidator(common.ListenerTypes)
+	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
+
+	// Validate arguments name and port
+	if len(args) < 2 {
+		validationErrors = append(validationErrors, fmt.Errorf("listener name and port must be configured"))
+	} else if len(args) > 2 {
+		validationErrors = append(validationErrors, fmt.Errorf("only two arguments are allowed for this command"))
+	} else if args[0] == "" {
+		validationErrors = append(validationErrors, fmt.Errorf("listener name must not be empty"))
+	} else if args[1] == "" {
+		validationErrors = append(validationErrors, fmt.Errorf("listener port must not be empty"))
+	} else {
+		ok, err := resourceStringValidator.Evaluate(args[0])
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("listener name is not valid: %s", err))
+		} else {
+			cmd.name = args[0]
+		}
+
+		cmd.port, err = strconv.Atoi(args[1])
+		if err != nil {
+			validationErrors = append(validationErrors, fmt.Errorf("listener port is not valid: %s", err))
+		}
+		ok, err = numberValidator.Evaluate(cmd.port)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("listener port is not valid: %s", err))
+		}
+	}
+
+	// Validate flags
+	if cmd.Flags != nil && cmd.Flags.RoutingKey != "" {
+		ok, err := resourceStringValidator.Evaluate(cmd.Flags.RoutingKey)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("routing key is not valid: %s", err))
+		}
+	}
+
+	if cmd.Flags != nil && cmd.Flags.TlsCredentials != "" {
+		ok, err := resourceStringValidator.Evaluate(cmd.Flags.TlsCredentials)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("tlsCredentials is not valid: %s", err))
+		}
+	}
+
+	if cmd.Flags != nil && cmd.Flags.ListenerType != "" {
+		ok, err := listenerTypeValidator.Evaluate(cmd.Flags.ListenerType)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("listener type is not valid: %s", err))
+		}
+	}
+
+	if cmd.Flags != nil && cmd.Flags.Output != "" {
+		ok, err := outputTypeValidator.Evaluate(cmd.Flags.Output)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("output type is not valid: %s", err))
+		}
+	}
+	return errors.Join(validationErrors...)
+}
+
+func (cmd *CmdListenerGenerate) 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.tlsCredentials = cmd.Flags.TlsCredentials
+	cmd.listenerType = cmd.Flags.ListenerType
+	cmd.output = cmd.Flags.Output
+}
+
+func (cmd *CmdListenerGenerate) Run() error {
+
+	resource := v2alpha1.Listener{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "Listener",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      cmd.name,
+			Namespace: cmd.namespace,
+		},
+		Spec: v2alpha1.ListenerSpec{
+			Host:           cmd.host,
+			Port:           cmd.port,
+			RoutingKey:     cmd.routingKey,
+			TlsCredentials: cmd.tlsCredentials,
+			Type:           cmd.listenerType,
+		},
+	}
+
+	encodedOutput, err := utils.Encode(cmd.output, resource)
+	fmt.Println(encodedOutput)
+	return err
+}
+
+func (cmd *CmdListenerGenerate) WaitUntil() error { return nil }
diff --git a/internal/cmd/skupper/listener/kube/listener_update.go b/internal/cmd/skupper/listener/kube/listener_update.go
index 030f2c8e..6dce8b9f 100644
--- a/internal/cmd/skupper/listener/kube/listener_update.go
+++ b/internal/cmd/skupper/listener/kube/listener_update.go
@@ -4,9 +4,10 @@ import (
 	"context"
 	"errors"
 	"fmt"
-	"k8s.io/apimachinery/pkg/api/meta"
 	"time"
 
+	"k8s.io/apimachinery/pkg/api/meta"
+
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 
@@ -27,8 +28,8 @@ type ListenerUpdates struct {
 	listenerType   string
 	port           int
 	timeout        time.Duration
-	output         string
 }
+
 type CmdListenerUpdate struct {
 	client          skupperv2alpha1.SkupperV2alpha1Interface
 	CobraCmd        *cobra.Command
@@ -60,7 +61,6 @@ func (cmd *CmdListenerUpdate) ValidateInput(args []string) error {
 	resourceStringValidator := validator.NewResourceStringValidator()
 	numberValidator := validator.NewNumberValidator()
 	listenerTypeValidator := validator.NewOptionValidator(common.ListenerTypes)
-	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
 	timeoutValidator := validator.NewTimeoutInSecondsValidator()
 	statusValidator := validator.NewOptionValidator(common.WaitStatusTypes)
 
@@ -138,14 +138,6 @@ func (cmd *CmdListenerUpdate) ValidateInput(args []string) error {
 			validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid: %s", err))
 		}
 	}
-	if cmd.Flags != nil && 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.newSettings.output = cmd.Flags.Output
-		}
-	}
 
 	if cmd.Flags != nil && cmd.Flags.Wait != "" {
 		ok, err := statusValidator.Evaluate(cmd.Flags.Wait)
@@ -177,23 +169,12 @@ func (cmd *CmdListenerUpdate) Run() error {
 		},
 	}
 
-	if cmd.newSettings.output != "" {
-		encodedOutput, err := utils.Encode(cmd.newSettings.output, resource)
-		fmt.Println(encodedOutput)
-		return err
-	} else {
-		_, err := cmd.client.Listeners(cmd.namespace).Update(context.TODO(), &resource, metav1.UpdateOptions{})
-		return err
-	}
+	_, err := cmd.client.Listeners(cmd.namespace).Update(context.TODO(), &resource, metav1.UpdateOptions{})
+	return err
 }
 
 func (cmd *CmdListenerUpdate) WaitUntil() error {
 
-	// the site resource was not created
-	if cmd.newSettings.output != "" {
-		return nil
-	}
-
 	if cmd.status == "none" {
 		return nil
 	}
diff --git a/internal/cmd/skupper/listener/listener.go b/internal/cmd/skupper/listener/listener.go
index a3d9ac29..6700218f 100644
--- a/internal/cmd/skupper/listener/listener.go
+++ b/internal/cmd/skupper/listener/listener.go
@@ -25,6 +25,7 @@ skupper listener status my-listener`,
 	cmd.AddCommand(CmdListenerStatusFactory(config.GetPlatform()))
 	cmd.AddCommand(CmdListenerUpdateFactory(config.GetPlatform()))
 	cmd.AddCommand(CmdListenerDeleteFactory(config.GetPlatform()))
+	cmd.AddCommand(CmdListenerGenerateFactory(config.GetPlatform()))
 
 	return cmd
 }
@@ -48,7 +49,7 @@ func CmdListenerCreateFactory(configuredPlatform types.Platform) *cobra.Command
 	cmd.Flags().StringVar(&cmdFlags.Host, common.FlagNameListenerHost, "", common.FlagDescListenerHost)
 	cmd.Flags().StringVar(&cmdFlags.TlsCredentials, common.FlagNameTlsCredentials, "", common.FlagDescTlsCredentials)
 	cmd.Flags().StringVar(&cmdFlags.ListenerType, common.FlagNameListenerType, "tcp", common.FlagDescListenerType)
-	cmd.Flags().StringVarP(&cmdFlags.Output, common.FlagNameOutput, "o", "", common.FlagDescOutput)
+
 	if configuredPlatform == types.PlatformKubernetes {
 		cmd.Flags().DurationVar(&cmdFlags.Timeout, common.FlagNameTimeout, 60*time.Second, common.FlagDescTimeout)
 		cmd.Flags().StringVar(&cmdFlags.Wait, common.FlagNameWait, "configured", common.FlagDescWait)
@@ -83,7 +84,6 @@ func CmdListenerUpdateFactory(configuredPlatform types.Platform) *cobra.Command
 	cmd.Flags().StringVarP(&cmdFlags.TlsCredentials, common.FlagNameTlsCredentials, "t", "", common.FlagDescTlsCredentials)
 	cmd.Flags().StringVar(&cmdFlags.ListenerType, common.FlagNameListenerType, "tcp", common.FlagDescListenerType)
 	cmd.Flags().IntVar(&cmdFlags.Port, common.FlagNameListenerPort, 0, common.FlagDescListenerPort)
-	cmd.Flags().StringVarP(&cmdFlags.Output, common.FlagNameOutput, "o", "", common.FlagDescOutput)
 	if configuredPlatform == types.PlatformKubernetes {
 		cmd.Flags().DurationVar(&cmdFlags.Timeout, common.FlagNameTimeout, 60*time.Second, common.FlagDescTimeout)
 		cmd.Flags().StringVar(&cmdFlags.Wait, common.FlagNameWait, "configured", common.FlagDescWait)
@@ -149,3 +149,33 @@ func CmdListenerDeleteFactory(configuredPlatform types.Platform) *cobra.Command
 
 	return cmd
 }
+
+func CmdListenerGenerateFactory(configuredPlatform types.Platform) *cobra.Command {
+	kubeCommand := kube.NewCmdListenerGenerate()
+	nonKubeCommand := nonkube.NewCmdListenerGenerate()
+
+	cmdListenerGenerateDesc := common.SkupperCmdDescription{
+		Use:   "generate <name> <port>",
+		Short: "generate a listener resource and output it to a file or screen",
+		Long: `Clients at this site use the listener host and port to establish connections to the remote service.
+	generate a listener to evaluate what will be created with listener create command`,
+		Example: "skupper listener generate database 5432",
+	}
+
+	cmd := common.ConfigureCobraCommand(configuredPlatform, cmdListenerGenerateDesc, kubeCommand, nonKubeCommand)
+
+	cmdFlags := common.CommandListenerGenerateFlags{}
+
+	cmd.Flags().StringVarP(&cmdFlags.RoutingKey, common.FlagNameRoutingKey, "r", "", common.FlagDescRoutingKey)
+	cmd.Flags().StringVar(&cmdFlags.Host, common.FlagNameListenerHost, "", common.FlagDescListenerHost)
+	cmd.Flags().StringVarP(&cmdFlags.TlsCredentials, common.FlagNameTlsCredentials, "t", "", common.FlagDescTlsCredentials)
+	cmd.Flags().StringVar(&cmdFlags.ListenerType, common.FlagNameListenerType, "tcp", common.FlagDescListenerType)
+	cmd.Flags().StringVarP(&cmdFlags.Output, common.FlagNameOutput, "o", "yaml", common.FlagDescOutput)
+
+	kubeCommand.CobraCmd = cmd
+	kubeCommand.Flags = &cmdFlags
+	nonKubeCommand.CobraCmd = cmd
+	nonKubeCommand.Flags = &cmdFlags
+
+	return cmd
+}
diff --git a/internal/cmd/skupper/listener/nonkube/listener_create.go b/internal/cmd/skupper/listener/nonkube/listener_create.go
index 7ba23bac..83ad4847 100644
--- a/internal/cmd/skupper/listener/nonkube/listener_create.go
+++ b/internal/cmd/skupper/listener/nonkube/listener_create.go
@@ -7,7 +7,6 @@ import (
 	"strconv"
 
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"github.com/skupperproject/skupper/internal/nonkube/client/fs"
 	"github.com/skupperproject/skupper/internal/utils/validator"
 	"github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
@@ -26,7 +25,6 @@ type CmdListenerCreate struct {
 	tlsCredentials  string
 	listenerType    string
 	routingKey      string
-	output          string
 }
 
 func NewCmdListenerCreate() *CmdListenerCreate {
@@ -55,7 +53,6 @@ func (cmd *CmdListenerCreate) ValidateInput(args []string) error {
 	resourceStringValidator := validator.NewResourceStringValidator()
 	numberValidator := validator.NewNumberValidator()
 	listenerTypeValidator := validator.NewOptionValidator(common.ListenerTypes)
-	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
 	hostStringValidator := validator.NewHostStringValidator()
 
 	// Validate arguments name and port
@@ -115,12 +112,6 @@ func (cmd *CmdListenerCreate) ValidateInput(args []string) error {
 		}
 	}
 
-	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))
-		}
-	}
 	return errors.Join(validationErrors...)
 }
 
@@ -144,7 +135,6 @@ func (cmd *CmdListenerCreate) InputToOptions() {
 
 	cmd.tlsCredentials = cmd.Flags.TlsCredentials
 	cmd.listenerType = cmd.Flags.ListenerType
-	cmd.output = cmd.Flags.Output
 }
 
 func (cmd *CmdListenerCreate) Run() error {
@@ -166,15 +156,9 @@ func (cmd *CmdListenerCreate) Run() error {
 		},
 	}
 
-	if cmd.output != "" {
-		encodedOutput, err := utils.Encode(cmd.output, listenerResource)
-		fmt.Println(encodedOutput)
+	err := cmd.listenerHandler.Add(listenerResource)
+	if err != nil {
 		return err
-	} else {
-		err := cmd.listenerHandler.Add(listenerResource)
-		if err != nil {
-			return err
-		}
 	}
 	return nil
 }
diff --git a/internal/cmd/skupper/listener/nonkube/listener_generate.go b/internal/cmd/skupper/listener/nonkube/listener_generate.go
new file mode 100644
index 00000000..303e3ae3
--- /dev/null
+++ b/internal/cmd/skupper/listener/nonkube/listener_generate.go
@@ -0,0 +1,175 @@
+package nonkube
+
+import (
+	"errors"
+	"fmt"
+	"net"
+	"strconv"
+
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
+	"github.com/skupperproject/skupper/internal/nonkube/client/fs"
+	"github.com/skupperproject/skupper/internal/utils/validator"
+	"github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
+	"github.com/spf13/cobra"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+type CmdListenerGenerate struct {
+	listenerHandler *fs.ListenerHandler
+	CobraCmd        *cobra.Command
+	Flags           *common.CommandListenerGenerateFlags
+	namespace       string
+	listenerName    string
+	port            int
+	host            string
+	tlsCredentials  string
+	listenerType    string
+	routingKey      string
+	output          string
+}
+
+func NewCmdListenerGenerate() *CmdListenerGenerate {
+	return &CmdListenerGenerate{}
+}
+
+func (cmd *CmdListenerGenerate) NewClient(cobraCommand *cobra.Command, args []string) {
+	if cmd.CobraCmd != nil && cmd.CobraCmd.Flag(common.FlagNameNamespace) != nil && cmd.CobraCmd.Flag(common.FlagNameNamespace).Value.String() != "" {
+		cmd.namespace = cmd.CobraCmd.Flag(common.FlagNameNamespace).Value.String()
+	}
+
+	cmd.listenerHandler = fs.NewListenerHandler(cmd.namespace)
+}
+
+func (cmd *CmdListenerGenerate) ValidateInput(args []string) error {
+	var validationErrors []error
+
+	if cmd.CobraCmd != nil && cmd.CobraCmd.Flag(common.FlagNameContext) != nil && cmd.CobraCmd.Flag(common.FlagNameContext).Value.String() != "" {
+		fmt.Println("Warning: --context flag is not supported on this platform")
+	}
+
+	if cmd.CobraCmd != nil && cmd.CobraCmd.Flag(common.FlagNameKubeconfig) != nil && cmd.CobraCmd.Flag(common.FlagNameKubeconfig).Value.String() != "" {
+		fmt.Println("Warning: --kubeconfig flag is not supported on this platform")
+	}
+
+	resourceStringValidator := validator.NewResourceStringValidator()
+	numberValidator := validator.NewNumberValidator()
+	listenerTypeValidator := validator.NewOptionValidator(common.ListenerTypes)
+	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
+	hostStringValidator := validator.NewHostStringValidator()
+
+	// Validate arguments name and port
+	if len(args) < 2 {
+		validationErrors = append(validationErrors, fmt.Errorf("listener name and port must be configured"))
+	} else if len(args) > 2 {
+		validationErrors = append(validationErrors, fmt.Errorf("only two arguments are allowed for this command"))
+	} else if args[0] == "" {
+		validationErrors = append(validationErrors, fmt.Errorf("listener name must not be empty"))
+	} else if args[1] == "" {
+		validationErrors = append(validationErrors, fmt.Errorf("listener port must not be empty"))
+	} else {
+		ok, err := resourceStringValidator.Evaluate(args[0])
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("listener name is not valid: %s", err))
+		} else {
+			cmd.listenerName = args[0]
+		}
+
+		cmd.port, err = strconv.Atoi(args[1])
+		if err != nil {
+			validationErrors = append(validationErrors, fmt.Errorf("listener port is not valid: %s", err))
+		}
+		ok, err = numberValidator.Evaluate(cmd.port)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("listener port is not valid: %s", err))
+		}
+	}
+
+	// Validate flags
+	if cmd.Flags.RoutingKey != "" {
+		ok, err := resourceStringValidator.Evaluate(cmd.Flags.RoutingKey)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("routing key is not valid: %s", err))
+		}
+	}
+
+	if cmd.Flags.ListenerType != "" {
+		ok, err := listenerTypeValidator.Evaluate(cmd.Flags.ListenerType)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("listener type is not valid: %s", err))
+		}
+	}
+
+	if cmd.Flags.Host != "" {
+		ip := net.ParseIP(cmd.Flags.Host)
+		ok, _ := hostStringValidator.Evaluate(cmd.Flags.Host)
+		if !ok && ip == nil {
+			validationErrors = append(validationErrors, fmt.Errorf("host is not valid: a valid IP address or hostname is expected"))
+		}
+	}
+
+	if cmd.Flags.TlsCredentials != "" {
+		ok, err := resourceStringValidator.Evaluate(cmd.Flags.TlsCredentials)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("tlsCredentials is not valid: %s", err))
+		}
+	}
+
+	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))
+		}
+	}
+	return errors.Join(validationErrors...)
+}
+
+func (cmd *CmdListenerGenerate) InputToOptions() {
+	// default routingkey to name of listener
+	if cmd.Flags.RoutingKey == "" {
+		cmd.routingKey = cmd.listenerName
+	} else {
+		cmd.routingKey = cmd.Flags.RoutingKey
+	}
+
+	if cmd.namespace == "" {
+		cmd.namespace = "default"
+	}
+
+	if cmd.Flags.Host == "" {
+		cmd.host = "0.0.0.0"
+	} else {
+		cmd.host = cmd.Flags.Host
+	}
+
+	cmd.tlsCredentials = cmd.Flags.TlsCredentials
+	cmd.listenerType = cmd.Flags.ListenerType
+	cmd.output = cmd.Flags.Output
+}
+
+func (cmd *CmdListenerGenerate) Run() error {
+	listenerResource := v2alpha1.Listener{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "Listener",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      cmd.listenerName,
+			Namespace: cmd.namespace,
+		},
+		Spec: v2alpha1.ListenerSpec{
+			Host:           cmd.host,
+			Port:           cmd.port,
+			RoutingKey:     cmd.routingKey,
+			TlsCredentials: cmd.tlsCredentials,
+			Type:           cmd.listenerType,
+		},
+	}
+
+	encodedOutput, err := utils.Encode(cmd.output, listenerResource)
+	fmt.Println(encodedOutput)
+	return err
+
+}
+
+func (cmd *CmdListenerGenerate) WaitUntil() error { return nil }
diff --git a/internal/cmd/skupper/listener/nonkube/listener_update.go b/internal/cmd/skupper/listener/nonkube/listener_update.go
index 76147444..26e0f753 100644
--- a/internal/cmd/skupper/listener/nonkube/listener_update.go
+++ b/internal/cmd/skupper/listener/nonkube/listener_update.go
@@ -6,7 +6,6 @@ import (
 	"net"
 
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"github.com/skupperproject/skupper/internal/nonkube/client/fs"
 	"github.com/skupperproject/skupper/internal/utils/validator"
 	"github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
@@ -20,7 +19,6 @@ type ListenerUpdates struct {
 	tlsCredentials string
 	listenerType   string
 	port           int
-	output         string
 }
 type CmdListenerUpdate struct {
 	listenerHandler *fs.ListenerHandler
@@ -49,7 +47,6 @@ func (cmd *CmdListenerUpdate) ValidateInput(args []string) error {
 	resourceStringValidator := validator.NewResourceStringValidator()
 	numberValidator := validator.NewNumberValidator()
 	listenerTypeValidator := validator.NewOptionValidator(common.ListenerTypes)
-	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
 	hostStringValidator := validator.NewHostStringValidator()
 
 	if cmd.CobraCmd != nil && cmd.CobraCmd.Flag(common.FlagNameContext) != nil && cmd.CobraCmd.Flag(common.FlagNameContext).Value.String() != "" {
@@ -133,14 +130,6 @@ func (cmd *CmdListenerUpdate) ValidateInput(args []string) error {
 			cmd.newSettings.port = cmd.Flags.Port
 		}
 	}
-	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.newSettings.output = cmd.Flags.Output
-		}
-	}
 
 	return errors.Join(validationErrors...)
 }
@@ -173,16 +162,12 @@ func (cmd *CmdListenerUpdate) Run() error {
 			Type:           cmd.newSettings.listenerType,
 		},
 	}
-	if cmd.newSettings.output != "" {
-		encodedOutput, err := utils.Encode(cmd.newSettings.output, listenerResource)
-		fmt.Println(encodedOutput)
+
+	err := cmd.listenerHandler.Add(listenerResource)
+	if err != nil {
 		return err
-	} else {
-		err := cmd.listenerHandler.Add(listenerResource)
-		if err != nil {
-			return err
-		}
 	}
+
 	return nil
 }
 
diff --git a/internal/cmd/skupper/site/kube/site_create.go b/internal/cmd/skupper/site/kube/site_create.go
index 014d1440..fa823530 100644
--- a/internal/cmd/skupper/site/kube/site_create.go
+++ b/internal/cmd/skupper/site/kube/site_create.go
@@ -7,9 +7,10 @@ import (
 	"context"
 	"errors"
 	"fmt"
-	"k8s.io/apimachinery/pkg/api/meta"
 	"time"
 
+	"k8s.io/apimachinery/pkg/api/meta"
+
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"github.com/skupperproject/skupper/internal/kube/client"
@@ -30,7 +31,6 @@ type CmdSiteCreate struct {
 	serviceAccountName string
 	Namespace          string
 	linkAccessType     string
-	output             string
 	timeout            time.Duration
 	status             string
 }
@@ -56,7 +56,6 @@ func (cmd *CmdSiteCreate) ValidateInput(args []string) error {
 	var validationErrors []error
 	resourceStringValidator := validator.NewResourceStringValidator()
 	linkAccessTypeValidator := validator.NewOptionValidator(common.LinkAccessTypes)
-	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
 	timeoutValidator := validator.NewTimeoutInSecondsValidator()
 	statusValidator := validator.NewOptionValidator(common.WaitStatusTypes)
 
@@ -66,10 +65,6 @@ func (cmd *CmdSiteCreate) ValidateInput(args []string) error {
 		validationErrors = append(validationErrors, fmt.Errorf("there is already a site created for this namespace"))
 	}
 
-	if cmd.Flags != nil && cmd.Flags.SubjectAlternativeNames != nil && len(cmd.Flags.SubjectAlternativeNames) > 0 {
-		fmt.Println("Warning: --subject-alternative-names flag is not supported on this platform")
-	}
-
 	if len(args) == 0 || args[0] == "" {
 		validationErrors = append(validationErrors, fmt.Errorf("site name must not be empty"))
 	} else if len(args) > 1 {
@@ -101,13 +96,6 @@ func (cmd *CmdSiteCreate) ValidateInput(args []string) error {
 		}
 	}
 
-	if cmd.Flags != nil && cmd.Flags.Output != "" {
-		ok, err := outputTypeValidator.Evaluate(cmd.Flags.Output)
-		if !ok {
-			validationErrors = append(validationErrors, fmt.Errorf("output type is not valid: %s", err))
-		}
-	}
-
 	if cmd.Flags != nil && cmd.Flags.Timeout.String() != "" {
 		ok, err := timeoutValidator.Evaluate(cmd.Flags.Timeout)
 		if !ok {
@@ -137,7 +125,6 @@ func (cmd *CmdSiteCreate) InputToOptions() {
 		}
 	}
 
-	cmd.output = cmd.Flags.Output
 	cmd.timeout = cmd.Flags.Timeout
 	cmd.status = cmd.Flags.Wait
 
@@ -160,26 +147,13 @@ func (cmd *CmdSiteCreate) Run() error {
 		},
 	}
 
-	if cmd.output != "" {
-		encodedOutput, err := utils.Encode(cmd.output, resource)
-		fmt.Println(encodedOutput)
-
-		return err
-
-	} else {
-		_, err := cmd.Client.Sites(cmd.Namespace).Create(context.TODO(), &resource, metav1.CreateOptions{})
-		return err
-	}
+	_, err := cmd.Client.Sites(cmd.Namespace).Create(context.TODO(), &resource, metav1.CreateOptions{})
+	return err
 
 }
 
 func (cmd *CmdSiteCreate) WaitUntil() error {
 
-	// the site resource was not created
-	if cmd.output != "" {
-		return nil
-	}
-
 	if cmd.status == "none" {
 		return nil
 	}
diff --git a/internal/cmd/skupper/site/kube/site_generate.go b/internal/cmd/skupper/site/kube/site_generate.go
new file mode 100644
index 00000000..b1033a3a
--- /dev/null
+++ b/internal/cmd/skupper/site/kube/site_generate.go
@@ -0,0 +1,132 @@
+/*
+Copyright © 2024 Skupper Team <skupper@googlegroups.com>
+*/
+package kube
+
+import (
+	"errors"
+	"fmt"
+
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
+	"github.com/skupperproject/skupper/internal/utils/validator"
+	"github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
+	skupperv2alpha1 "github.com/skupperproject/skupper/pkg/generated/client/clientset/versioned/typed/skupper/v2alpha1"
+	"github.com/spf13/cobra"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/client-go/kubernetes"
+)
+
+type CmdSiteGenerate struct {
+	Client             skupperv2alpha1.SkupperV2alpha1Interface
+	KubeClient         kubernetes.Interface
+	CobraCmd           *cobra.Command
+	Flags              *common.CommandSiteGenerateFlags
+	siteName           string
+	serviceAccountName string
+	Namespace          string
+	linkAccessType     string
+	output             string
+}
+
+func NewCmdSiteGenerate() *CmdSiteGenerate {
+
+	skupperCmd := CmdSiteGenerate{}
+
+	return &skupperCmd
+}
+
+func (cmd *CmdSiteGenerate) NewClient(cobraCommand *cobra.Command, args []string) {
+
+	cmd.Namespace = cobraCommand.Flag("namespace").Value.String()
+
+}
+
+func (cmd *CmdSiteGenerate) ValidateInput(args []string) error {
+	var validationErrors []error
+	resourceStringValidator := validator.NewResourceStringValidator()
+	linkAccessTypeValidator := validator.NewOptionValidator(common.LinkAccessTypes)
+	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
+
+	if len(args) == 0 || args[0] == "" {
+		validationErrors = append(validationErrors, fmt.Errorf("site name must not be empty"))
+	} else if len(args) > 1 {
+		validationErrors = append(validationErrors, fmt.Errorf("only one argument is allowed for this command."))
+	} else {
+		cmd.siteName = args[0]
+
+		ok, err := resourceStringValidator.Evaluate(cmd.siteName)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("site name is not valid: %s", err))
+		}
+	}
+
+	if cmd.Flags != nil && cmd.Flags.LinkAccessType != "" {
+		ok, err := linkAccessTypeValidator.Evaluate(cmd.Flags.LinkAccessType)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("link access type is not valid: %s", err))
+		}
+	}
+
+	if cmd.Flags != nil && !cmd.Flags.EnableLinkAccess && len(cmd.Flags.LinkAccessType) > 0 {
+		validationErrors = append(validationErrors, fmt.Errorf("for the site to work with this type of linkAccess, the --enable-link-access option must be set to true"))
+	}
+
+	if cmd.Flags != nil && cmd.Flags.ServiceAccount != "" {
+		ok, err := resourceStringValidator.Evaluate(cmd.Flags.ServiceAccount)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("service account name is not valid: %s", err))
+		}
+	}
+	if cmd.Flags != nil && cmd.Flags.Output != "" {
+		ok, err := outputTypeValidator.Evaluate(cmd.Flags.Output)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("format %s", err))
+		}
+	}
+
+	return errors.Join(validationErrors...)
+}
+
+func (cmd *CmdSiteGenerate) InputToOptions() {
+
+	cmd.serviceAccountName = cmd.Flags.ServiceAccount
+
+	if cmd.Flags.EnableLinkAccess {
+		if cmd.Flags.LinkAccessType == "" {
+			cmd.linkAccessType = "default"
+		} else {
+			cmd.linkAccessType = cmd.Flags.LinkAccessType
+		}
+	}
+
+	if cmd.Flags.Output != "" {
+		cmd.output = cmd.Flags.Output
+	} else {
+		cmd.output = "yaml"
+	}
+}
+
+func (cmd *CmdSiteGenerate) Run() error {
+
+	resource := v2alpha1.Site{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "Site",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      cmd.siteName,
+			Namespace: cmd.Namespace,
+		},
+		Spec: v2alpha1.SiteSpec{
+			ServiceAccount: cmd.serviceAccountName,
+			LinkAccess:     cmd.linkAccessType,
+		},
+	}
+
+	encodedOutput, err := utils.Encode(cmd.output, resource)
+	fmt.Println(encodedOutput)
+	return err
+}
+
+func (cmd *CmdSiteGenerate) WaitUntil() error { return nil }
diff --git a/internal/cmd/skupper/site/kube/site_update.go b/internal/cmd/skupper/site/kube/site_update.go
index c49365fd..cf671447 100644
--- a/internal/cmd/skupper/site/kube/site_update.go
+++ b/internal/cmd/skupper/site/kube/site_update.go
@@ -28,7 +28,6 @@ type CmdSiteUpdate struct {
 	serviceAccountName string
 	Namespace          string
 	linkAccessType     string
-	output             string
 	timeout            time.Duration
 	status             string
 }
@@ -53,7 +52,6 @@ func (cmd *CmdSiteUpdate) ValidateInput(args []string) error {
 
 	var validationErrors []error
 	linkAccessTypeValidator := validator.NewOptionValidator(common.LinkAccessTypes)
-	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
 	timeoutValidator := validator.NewTimeoutInSecondsValidator()
 	statusValidator := validator.NewOptionValidator(common.WaitStatusTypes)
 
@@ -87,7 +85,7 @@ func (cmd *CmdSiteUpdate) ValidateInput(args []string) error {
 	}
 
 	if cmd.Flags != nil && cmd.Flags.BindHost != "" {
-		validationErrors = append(validationErrors, fmt.Errorf("--host flag is not supported on this platform"))
+		validationErrors = append(validationErrors, fmt.Errorf("--bind-host flag is not supported on this platform"))
 	}
 
 	if cmd.Flags.LinkAccessType != "" {
@@ -108,13 +106,6 @@ func (cmd *CmdSiteUpdate) ValidateInput(args []string) error {
 		}
 	}
 
-	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))
-		}
-	}
-
 	if cmd.Flags != nil && cmd.Flags.Timeout.String() != "" {
 		ok, err := timeoutValidator.Evaluate(cmd.Flags.Timeout)
 		if !ok {
@@ -142,7 +133,6 @@ func (cmd *CmdSiteUpdate) InputToOptions() {
 		}
 	}
 
-	cmd.output = cmd.Flags.Output
 	cmd.timeout = cmd.Flags.Timeout
 	cmd.status = cmd.Flags.Wait
 
@@ -186,25 +176,12 @@ func (cmd *CmdSiteUpdate) Run() error {
 		Status: currentSite.Status,
 	}
 
-	if cmd.output != "" {
-		encodedOutput, err := utils.Encode(cmd.output, resource)
-		fmt.Println(encodedOutput)
-
-		return err
-
-	} else {
-		_, err := cmd.Client.Sites(cmd.Namespace).Update(context.TODO(), &resource, metav1.UpdateOptions{})
-		return err
-	}
+	_, err = cmd.Client.Sites(cmd.Namespace).Update(context.TODO(), &resource, metav1.UpdateOptions{})
+	return err
 }
 
 func (cmd *CmdSiteUpdate) WaitUntil() error {
 
-	// the site resource was not updated
-	if cmd.output != "" {
-		return nil
-	}
-
 	if cmd.status == "none" {
 		return nil
 	}
diff --git a/internal/cmd/skupper/site/nonkube/site_create.go b/internal/cmd/skupper/site/nonkube/site_create.go
index 18c0a795..d956878b 100644
--- a/internal/cmd/skupper/site/nonkube/site_create.go
+++ b/internal/cmd/skupper/site/nonkube/site_create.go
@@ -9,7 +9,6 @@ import (
 	"net"
 
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"github.com/skupperproject/skupper/internal/nonkube/client/fs"
 	"github.com/skupperproject/skupper/internal/utils/validator"
 	"github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
@@ -25,7 +24,6 @@ type CmdSiteCreate struct {
 	options                 map[string]string
 	siteName                string
 	linkAccessEnabled       bool
-	output                  string
 	namespace               string
 	bindHost                string
 	routerAccessName        string
@@ -50,7 +48,6 @@ func (cmd *CmdSiteCreate) ValidateInput(args []string) error {
 	var validationErrors []error
 	hostStringValidator := validator.NewHostStringValidator()
 	resourceStringValidator := validator.NewResourceStringValidator()
-	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
 
 	if cmd.Flags.ServiceAccount != "" {
 		fmt.Println("Warning: --service-account flag is not supported on this platform")
@@ -76,13 +73,6 @@ func (cmd *CmdSiteCreate) ValidateInput(args []string) error {
 		cmd.siteName = args[0]
 	}
 
-	if cmd.Flags != nil && cmd.Flags.Output != "" {
-		ok, err := outputTypeValidator.Evaluate(cmd.Flags.Output)
-		if !ok {
-			validationErrors = append(validationErrors, fmt.Errorf("output type is not valid: %s", err))
-		}
-	}
-
 	if cmd.Flags != nil && cmd.Flags.BindHost != "" {
 		ip := net.ParseIP(cmd.Flags.BindHost)
 		ok, _ := hostStringValidator.Evaluate(cmd.Flags.BindHost)
@@ -115,7 +105,6 @@ func (cmd *CmdSiteCreate) InputToOptions() {
 	options[common.SiteConfigNameKey] = cmd.siteName
 
 	cmd.options = options
-	cmd.output = cmd.Flags.Output
 
 	if cmd.namespace == "" {
 		cmd.namespace = "default"
@@ -163,33 +152,16 @@ func (cmd *CmdSiteCreate) Run() error {
 		},
 	}
 
-	if cmd.output != "" {
-		encodedSiteOutput, err := utils.Encode(cmd.output, siteResource)
-		if err != nil {
-			return err
-		}
-		fmt.Println(encodedSiteOutput)
-		if cmd.linkAccessEnabled == true {
-			fmt.Println("---")
-			encodedRouterAccessOutput, err := utils.Encode(cmd.output, routerAccessResource)
-			if err != nil {
-				return err
-			}
-			fmt.Println(encodedRouterAccessOutput)
-		}
-	} else {
-		err := cmd.siteHandler.Add(siteResource)
+	err := cmd.siteHandler.Add(siteResource)
+	if err != nil {
+		return err
+	}
+
+	if cmd.linkAccessEnabled == true {
+		err = cmd.routerAccessHandler.Add(routerAccessResource)
 		if err != nil {
 			return err
 		}
-
-		if cmd.linkAccessEnabled == true {
-			err = cmd.routerAccessHandler.Add(routerAccessResource)
-			if err != nil {
-				return err
-			}
-		}
-
 	}
 
 	return nil
diff --git a/internal/cmd/skupper/site/nonkube/site_generate.go b/internal/cmd/skupper/site/nonkube/site_generate.go
new file mode 100644
index 00000000..e3fade28
--- /dev/null
+++ b/internal/cmd/skupper/site/nonkube/site_generate.go
@@ -0,0 +1,187 @@
+/*
+Copyright © 2024 Skupper Team <skupper@googlegroups.com>
+*/
+package nonkube
+
+import (
+	"errors"
+	"fmt"
+	"net"
+
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
+	"github.com/skupperproject/skupper/internal/nonkube/client/fs"
+	"github.com/skupperproject/skupper/internal/utils/validator"
+	"github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
+	"github.com/spf13/cobra"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+type CmdSiteGenerate struct {
+	siteHandler             *fs.SiteHandler
+	routerAccessHandler     *fs.RouterAccessHandler
+	CobraCmd                *cobra.Command
+	Flags                   *common.CommandSiteGenerateFlags
+	options                 map[string]string
+	siteName                string
+	linkAccessEnabled       bool
+	output                  string
+	namespace               string
+	bindHost                string
+	routerAccessName        string
+	subjectAlternativeNames []string
+}
+
+func NewCmdSiteGenerate() *CmdSiteGenerate {
+	return &CmdSiteGenerate{}
+}
+
+func (cmd *CmdSiteGenerate) NewClient(cobraCommand *cobra.Command, args []string) {
+	if cmd.CobraCmd != nil && cmd.CobraCmd.Flag(common.FlagNameNamespace) != nil && cmd.CobraCmd.Flag(common.FlagNameNamespace).Value.String() != "" {
+		cmd.namespace = cmd.CobraCmd.Flag(common.FlagNameNamespace).Value.String()
+	}
+
+	cmd.siteHandler = fs.NewSiteHandler(cmd.namespace)
+	cmd.routerAccessHandler = fs.NewRouterAccessHandler(cmd.namespace)
+
+}
+
+func (cmd *CmdSiteGenerate) ValidateInput(args []string) error {
+
+	var validationErrors []error
+	resourceStringValidator := validator.NewResourceStringValidator()
+	hostStringValidator := validator.NewHostStringValidator()
+	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
+
+	if cmd.Flags.ServiceAccount != "" {
+		fmt.Println("Warning: --service-account flag is not supported on this platform")
+	}
+
+	if cmd.CobraCmd != nil && cmd.CobraCmd.Flag(common.FlagNameContext) != nil && cmd.CobraCmd.Flag(common.FlagNameContext).Value.String() != "" {
+		fmt.Println("Warning: --context flag is not supported on this platform")
+	}
+
+	if cmd.CobraCmd != nil && cmd.CobraCmd.Flag(common.FlagNameKubeconfig) != nil && cmd.CobraCmd.Flag(common.FlagNameKubeconfig).Value.String() != "" {
+		fmt.Println("Warning: --kubeconfig flag is not supported on this platform")
+	}
+
+	if len(args) == 0 || args[0] == "" {
+		validationErrors = append(validationErrors, fmt.Errorf("site name must not be empty"))
+	} else if len(args) > 1 {
+		validationErrors = append(validationErrors, fmt.Errorf("only one argument is allowed for this command"))
+	} else {
+		ok, err := resourceStringValidator.Evaluate(args[0])
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("site name is not valid: %s", err))
+		}
+		cmd.siteName = args[0]
+
+	}
+
+	if cmd.Flags != nil && cmd.Flags.BindHost != "" {
+		ip := net.ParseIP(cmd.Flags.BindHost)
+		ok, _ := hostStringValidator.Evaluate(cmd.Flags.BindHost)
+		if !ok && ip == nil {
+			validationErrors = append(validationErrors, fmt.Errorf("bindhost is not valid: a valid IP address or hostname is expected"))
+		}
+	}
+	if cmd.Flags != nil && len(cmd.Flags.SubjectAlternativeNames) != 0 {
+		for _, name := range cmd.Flags.SubjectAlternativeNames {
+			ip := net.ParseIP(name)
+			ok, _ := hostStringValidator.Evaluate(name)
+			if !ok && ip == nil {
+				validationErrors = append(validationErrors, fmt.Errorf("SubjectAlternativeNames is not valid: a valid IP address or hostname is expected"))
+			}
+		}
+	}
+
+	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))
+		}
+	}
+
+	return errors.Join(validationErrors...)
+}
+
+func (cmd *CmdSiteGenerate) InputToOptions() {
+	if cmd.Flags.EnableLinkAccess {
+		cmd.linkAccessEnabled = true
+		cmd.bindHost = cmd.Flags.BindHost
+		cmd.routerAccessName = "router-access-" + cmd.siteName
+		cmd.subjectAlternativeNames = cmd.Flags.SubjectAlternativeNames
+	}
+	options := make(map[string]string)
+	options[common.SiteConfigNameKey] = cmd.siteName
+
+	cmd.options = options
+
+	if cmd.namespace == "" {
+		cmd.namespace = "default"
+	}
+
+	cmd.output = cmd.Flags.Output
+
+}
+
+func (cmd *CmdSiteGenerate) Run() error {
+
+	siteResource := v2alpha1.Site{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "Site",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      cmd.siteName,
+			Namespace: cmd.namespace,
+		},
+		Spec: v2alpha1.SiteSpec{
+			Settings: cmd.options,
+		},
+	}
+
+	routerAccessResource := v2alpha1.RouterAccess{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "RouterAccess",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      cmd.routerAccessName,
+			Namespace: cmd.namespace,
+		},
+		Spec: v2alpha1.RouterAccessSpec{
+			Roles: []v2alpha1.RouterAccessRole{
+				{
+					Name: "inter-router",
+					Port: 55671,
+				},
+				{
+					Name: "edge",
+					Port: 45671,
+				},
+			},
+			BindHost:                cmd.bindHost,
+			SubjectAlternativeNames: cmd.subjectAlternativeNames,
+		},
+	}
+
+	encodedSiteOutput, err := utils.Encode(cmd.output, siteResource)
+	if err != nil {
+		return err
+	}
+	fmt.Println(encodedSiteOutput)
+
+	if cmd.linkAccessEnabled == true {
+		fmt.Println("---")
+		encodedRouterAccessOutput, err := utils.Encode(cmd.output, routerAccessResource)
+		if err != nil {
+			return err
+		}
+		fmt.Println(encodedRouterAccessOutput)
+	}
+
+	return err
+}
+
+func (cmd *CmdSiteGenerate) WaitUntil() error { return nil }
diff --git a/internal/cmd/skupper/site/nonkube/site_update.go b/internal/cmd/skupper/site/nonkube/site_update.go
index 26de4f7a..d408811f 100644
--- a/internal/cmd/skupper/site/nonkube/site_update.go
+++ b/internal/cmd/skupper/site/nonkube/site_update.go
@@ -6,7 +6,6 @@ import (
 	"net"
 
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"github.com/skupperproject/skupper/internal/nonkube/client/fs"
 	"github.com/skupperproject/skupper/internal/utils/validator"
 	"github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
@@ -30,7 +29,6 @@ type CmdSiteUpdate struct {
 	namespace               string
 	linkAccessEnabled       bool
 	bindHost                string
-	output                  string
 	routerAccessName        string
 	subjectAlternativeNames []string
 	newSettings             SiteUpdates
@@ -53,7 +51,6 @@ func (cmd *CmdSiteUpdate) ValidateInput(args []string) error {
 	var validationErrors []error
 	opts := fs.GetOptions{RuntimeFirst: false, LogWarning: false}
 	resourceStringValidator := validator.NewResourceStringValidator()
-	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
 	hostStringValidator := validator.NewHostStringValidator()
 
 	if cmd.Flags.ServiceAccount != "" {
@@ -125,12 +122,6 @@ func (cmd *CmdSiteUpdate) ValidateInput(args []string) error {
 			}
 		}
 	}
-	if cmd.Flags != nil && cmd.Flags.Output != "" {
-		ok, err := outputTypeValidator.Evaluate(cmd.Flags.Output)
-		if !ok {
-			validationErrors = append(validationErrors, fmt.Errorf("output type is not valid: %s", err))
-		}
-	}
 
 	return errors.Join(validationErrors...)
 }
@@ -158,7 +149,6 @@ func (cmd *CmdSiteUpdate) InputToOptions() {
 	options[common.SiteConfigNameKey] = cmd.siteName
 
 	cmd.options = options
-	cmd.output = cmd.Flags.Output
 	cmd.routerAccessName = "router-access-" + cmd.siteName
 
 	if cmd.namespace == "" {
@@ -207,39 +197,22 @@ func (cmd *CmdSiteUpdate) Run() error {
 		},
 	}
 
-	if cmd.output != "" {
-		encodedSiteOutput, err := utils.Encode(cmd.output, siteResource)
+	err := cmd.siteHandler.Add(siteResource)
+	if err != nil {
+		return err
+	}
+
+	if cmd.linkAccessEnabled == true {
+		err = cmd.routerAccessHandler.Add(routerAccessResource)
 		if err != nil {
 			return err
 		}
-		fmt.Println(encodedSiteOutput)
-		if cmd.linkAccessEnabled == true {
-			fmt.Println("---")
-			encodedRouterAccessOutput, err := utils.Encode(cmd.output, routerAccessResource)
-			if err != nil {
-				return err
-			}
-			fmt.Println(encodedRouterAccessOutput)
-		}
-		return err
 	} else {
-		err := cmd.siteHandler.Add(siteResource)
+		fmt.Println("link access not enabled, router access removed", cmd.routerAccessName)
+		err = cmd.routerAccessHandler.Delete(cmd.routerAccessName)
 		if err != nil {
 			return err
 		}
-
-		if cmd.linkAccessEnabled == true {
-			err = cmd.routerAccessHandler.Add(routerAccessResource)
-			if err != nil {
-				return err
-			}
-		} else {
-			fmt.Println("link access not enabled, router access removed", cmd.routerAccessName)
-			err = cmd.routerAccessHandler.Delete(cmd.routerAccessName)
-			if err != nil {
-				return err
-			}
-		}
 	}
 
 	return nil
diff --git a/internal/cmd/skupper/site/site.go b/internal/cmd/skupper/site/site.go
index 158397ad..99e86e71 100644
--- a/internal/cmd/skupper/site/site.go
+++ b/internal/cmd/skupper/site/site.go
@@ -28,6 +28,7 @@ skupper site status`,
 	cmd.AddCommand(CmdSiteStatusFactory(config.GetPlatform()))
 	cmd.AddCommand(CmdSiteDeleteFactory(config.GetPlatform()))
 	cmd.AddCommand(CmdSiteUpdateFactory(config.GetPlatform()))
+	cmd.AddCommand(CmdSiteGenerateFactory(config.GetPlatform()))
 
 	return cmd
 }
@@ -51,7 +52,6 @@ There can be only one site definition per namespace.`,
 
 	cmd.Flags().BoolVar(&cmdFlags.EnableLinkAccess, common.FlagNameEnableLinkAccess, false, common.FlagDescEnableLinkAccess)
 	cmd.Flags().StringVar(&cmdFlags.LinkAccessType, common.FlagNameLinkAccessType, "", common.FlagDescLinkAccessType)
-	cmd.Flags().StringVarP(&cmdFlags.Output, common.FlagNameOutput, "o", "", common.FlagDescOutput)
 	cmd.Flags().StringVar(&cmdFlags.ServiceAccount, common.FlagNameServiceAccount, "", common.FlagDescServiceAccount)
 	cmd.Flags().DurationVar(&cmdFlags.Timeout, common.FlagNameTimeout, 3*time.Minute, common.FlagDescTimeout)
 	cmd.Flags().StringVar(&cmdFlags.BindHost, common.FlagNameBindHost, "0.0.0.0", common.FlagDescBindHost)
@@ -91,7 +91,6 @@ func CmdSiteUpdateFactory(configuredPlatform types.Platform) *cobra.Command {
 
 	cmd.Flags().BoolVar(&cmdFlags.EnableLinkAccess, common.FlagNameEnableLinkAccess, false, common.FlagDescEnableLinkAccess)
 	cmd.Flags().StringVar(&cmdFlags.LinkAccessType, common.FlagNameLinkAccessType, "", common.FlagDescLinkAccessType)
-	cmd.Flags().StringVarP(&cmdFlags.Output, common.FlagNameOutput, "o", "", common.FlagDescOutput)
 	cmd.Flags().StringVar(&cmdFlags.ServiceAccount, common.FlagNameServiceAccount, "", common.FlagDescServiceAccount)
 	cmd.Flags().DurationVar(&cmdFlags.Timeout, common.FlagNameTimeout, 30*time.Second, common.FlagDescTimeout)
 	cmd.Flags().StringVar(&cmdFlags.BindHost, common.FlagNameBindHost, "", common.FlagDescBindHost)
@@ -166,3 +165,45 @@ skupper site delete --wait=false`,
 
 	return cmd
 }
+
+func CmdSiteGenerateFactory(configuredPlatform types.Platform) *cobra.Command {
+	kubeCommand := kube.NewCmdSiteGenerate()
+	nonKubeCommand := nonkube.NewCmdSiteGenerate()
+
+	cmdSiteGenerateDesc := common.SkupperCmdDescription{
+		Use:   "generate <name>",
+		Short: "Generate a site resource and output it to a file or screen",
+		Long: `A site is a place where components of your application are running.
+Sites are linked to form application networks.
+There can be only one site definition per namespace.
+Generate a site resource to evaluate what will be created with the site create command`,
+		Example: "skupper site generate my-site --enable-link-access",
+	}
+
+	cmd := common.ConfigureCobraCommand(configuredPlatform, cmdSiteGenerateDesc, kubeCommand, nonKubeCommand)
+
+	cmdFlags := common.CommandSiteGenerateFlags{}
+
+	cmd.Flags().BoolVar(&cmdFlags.EnableLinkAccess, common.FlagNameEnableLinkAccess, false, common.FlagDescEnableLinkAccess)
+	cmd.Flags().StringVar(&cmdFlags.LinkAccessType, common.FlagNameLinkAccessType, "", common.FlagDescLinkAccessType)
+	cmd.Flags().StringVarP(&cmdFlags.Output, common.FlagNameOutput, "o", "yaml", common.FlagDescOutput)
+	cmd.Flags().StringVar(&cmdFlags.ServiceAccount, common.FlagNameServiceAccount, "", common.FlagDescServiceAccount)
+	cmd.Flags().StringVar(&cmdFlags.BindHost, common.FlagNameBindHost, "0.0.0.0", common.FlagDescBindHost)
+	cmd.Flags().StringSliceVar(&cmdFlags.SubjectAlternativeNames, common.FlagNameSubjectAlternativeNames, []string{}, common.FlagDescSubjectAlternativeNames)
+
+	kubeCommand.CobraCmd = cmd
+	kubeCommand.Flags = &cmdFlags
+	nonKubeCommand.CobraCmd = cmd
+	nonKubeCommand.Flags = &cmdFlags
+
+	if configuredPlatform == types.PlatformKubernetes {
+		cmd.Flags().MarkHidden(common.FlagNameServiceAccount)
+	} else {
+		cmd.Flags().MarkHidden(common.FlagNameBindHost)
+		cmd.Flags().MarkHidden(common.FlagNameSubjectAlternativeNames)
+
+	}
+
+	return cmd
+
+}
diff --git a/internal/utils/validator/simple_validator.go b/internal/utils/validator/simple_validator.go
index 22275243..d4acbfed 100644
--- a/internal/utils/validator/simple_validator.go
+++ b/internal/utils/validator/simple_validator.go
@@ -201,6 +201,7 @@ func (s WorkloadValidator) Evaluate(value interface{}) (string, string, bool, er
 				return option, resource[1], true, nil
 			}
 		}
+		return "", "", false, fmt.Errorf("resource-type does not match expected value: deployment/service/daemonset/statefulset")
 	}
 	return "", "", false, fmt.Errorf("value does not match this regular expression: %s", s.Expression)
 }


Test patch:
diff --git a/internal/cmd/skupper/connector/connector_test.go b/internal/cmd/skupper/connector/connector_test.go
index bd79a342..b2c52f51 100644
--- a/internal/cmd/skupper/connector/connector_test.go
+++ b/internal/cmd/skupper/connector/connector_test.go
@@ -30,7 +30,6 @@ func TestCmdConnectorFactory(t *testing.T) {
 				common.FlagNameIncludeNotReadyPods: "false",
 				common.FlagNameSelector:            "",
 				common.FlagNameWorkload:            "",
-				common.FlagNameOutput:              "",
 				common.FlagNameTimeout:             "1m0s",
 				common.FlagNameWait:                "configured",
 			},
@@ -46,7 +45,6 @@ func TestCmdConnectorFactory(t *testing.T) {
 				common.FlagNameIncludeNotReadyPods: "false",
 				common.FlagNameSelector:            "",
 				common.FlagNameWorkload:            "",
-				common.FlagNameOutput:              "",
 				common.FlagNameTimeout:             "1m0s",
 				common.FlagNameConnectorPort:       "0",
 				common.FlagNameWait:                "configured",
@@ -68,6 +66,20 @@ func TestCmdConnectorFactory(t *testing.T) {
 			},
 			command: CmdConnectorDeleteFactory(types.PlatformKubernetes),
 		},
+		{
+			name: "CmdConnectorGenerateFactory",
+			expectedFlagsWithDefaultValue: map[string]interface{}{
+				common.FlagNameRoutingKey:          "",
+				common.FlagNameHost:                "",
+				common.FlagNameTlsCredentials:      "",
+				common.FlagNameConnectorType:       "tcp",
+				common.FlagNameIncludeNotReadyPods: "false",
+				common.FlagNameSelector:            "",
+				common.FlagNameWorkload:            "",
+				common.FlagNameOutput:              "yaml",
+			},
+			command: CmdConnectorGenerateFactory(types.PlatformKubernetes),
+		},
 	}
 
 	for _, test := range testTable {
diff --git a/internal/cmd/skupper/connector/kube/connector_create_test.go b/internal/cmd/skupper/connector/kube/connector_create_test.go
index 2fae1ee0..c0cfbfd3 100644
--- a/internal/cmd/skupper/connector/kube/connector_create_test.go
+++ b/internal/cmd/skupper/connector/kube/connector_create_test.go
@@ -199,21 +199,19 @@ func TestCmdConnectorCreate_ValidateInput(t *testing.T) {
 			expectedError: "timeout is not valid: duration must not be less than 10s; got 0s",
 		},
 		{
-			name: "output is not valid",
-			args: []string{"bad-output", "1234"},
+			name: "host is not valid",
+			args: []string{"my-connector-host", "8080"},
 			flags: common.CommandConnectorCreateFlags{
-				Host:    "host",
+				Host:    "not-valid$",
 				Timeout: 10 * time.Second,
-				Output:  "not-supported",
 			},
-			expectedError: "output type is not valid: value not-supported not allowed. It should be one of this options: [json yaml]",
+			expectedError: "host is not valid: a valid IP address or hostname is expected",
 		},
 		{
 			name: "selector/host",
 			args: []string{"selector", "1234"},
 			flags: common.CommandConnectorCreateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Selector: "app=test",
 				Host:     "test",
 			},
@@ -225,7 +223,6 @@ func TestCmdConnectorCreate_ValidateInput(t *testing.T) {
 			args: []string{"workload", "1234"},
 			flags: common.CommandConnectorCreateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "deployment/test",
 				Host:     "test",
 			},
@@ -282,7 +279,6 @@ func TestCmdConnectorCreate_ValidateWorkload(t *testing.T) {
 			name: "workload-no-deployment",
 			args: []string{"workload-deployment", "1234"}, flags: common.CommandConnectorCreateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "deployment/backend",
 			},
 			expectedError: "failed trying to get Deployment specified by workload: deployments.apps \"backend\" not found",
@@ -291,7 +287,6 @@ func TestCmdConnectorCreate_ValidateWorkload(t *testing.T) {
 			name: "workload-deployment-no-labels",
 			args: []string{"workload-deployment-no-labels", "1234"}, flags: common.CommandConnectorCreateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "deployment/backend",
 			},
 			k8sObjects: []runtime.Object{
@@ -315,7 +310,6 @@ func TestCmdConnectorCreate_ValidateWorkload(t *testing.T) {
 			name: "workload-deployment",
 			args: []string{"workload-deployment", "1234"}, flags: common.CommandConnectorCreateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "deployment/backend",
 			},
 			k8sObjects: []runtime.Object{
@@ -344,7 +338,6 @@ func TestCmdConnectorCreate_ValidateWorkload(t *testing.T) {
 			name: "workload-no-service",
 			args: []string{"workload-no-service", "1234"}, flags: common.CommandConnectorCreateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "service/backend",
 			},
 			expectedError: "failed trying to get Service specified by workload: services \"backend\" not found",
@@ -353,7 +346,6 @@ func TestCmdConnectorCreate_ValidateWorkload(t *testing.T) {
 			name: "workload-service-no-labels",
 			args: []string{"workload-service-no-labels", "1234"}, flags: common.CommandConnectorCreateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "service/backend",
 			},
 			k8sObjects: []runtime.Object{
@@ -375,7 +367,6 @@ func TestCmdConnectorCreate_ValidateWorkload(t *testing.T) {
 			name: "workload-service",
 			args: []string{"workload-service", "1234"}, flags: common.CommandConnectorCreateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "service/backend",
 			},
 			k8sObjects: []runtime.Object{
@@ -402,7 +393,6 @@ func TestCmdConnectorCreate_ValidateWorkload(t *testing.T) {
 			name: "workload-no-daemonset",
 			args: []string{"workload-no-daemonset", "1234"}, flags: common.CommandConnectorCreateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "daemonset/backend",
 			},
 			expectedError: "failed trying to get DaemonSet specified by workload: daemonsets.apps \"backend\" not found",
@@ -411,7 +401,6 @@ func TestCmdConnectorCreate_ValidateWorkload(t *testing.T) {
 			name: "workload-daemonset-no-labels",
 			args: []string{"workload-daemonset-no-labels", "1234"}, flags: common.CommandConnectorCreateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "daemonset/backend",
 			},
 			k8sObjects: []runtime.Object{
@@ -435,7 +424,6 @@ func TestCmdConnectorCreate_ValidateWorkload(t *testing.T) {
 			name: "workload-daemonset",
 			args: []string{"workload-daemonset", "1234"}, flags: common.CommandConnectorCreateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "DaemonSet/backend",
 			},
 			k8sObjects: []runtime.Object{
@@ -464,7 +452,6 @@ func TestCmdConnectorCreate_ValidateWorkload(t *testing.T) {
 			name: "workload-no-statefulset",
 			args: []string{"workload-no-statefulset", "1234"}, flags: common.CommandConnectorCreateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "StatefulSet/backend",
 			},
 			expectedError: "failed trying to get StatefulSet specified by workload: statefulsets.apps \"backend\" not found",
@@ -473,7 +460,6 @@ func TestCmdConnectorCreate_ValidateWorkload(t *testing.T) {
 			name: "workload-statefulset-no-labels",
 			args: []string{"workload-statefulset-no-labels", "1234"}, flags: common.CommandConnectorCreateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "statefulset/backend",
 			},
 			k8sObjects: []runtime.Object{
@@ -497,7 +483,6 @@ func TestCmdConnectorCreate_ValidateWorkload(t *testing.T) {
 			name: "workload-statefulset",
 			args: []string{"workload-statefulset", "1234"}, flags: common.CommandConnectorCreateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "statefulset/backend",
 			},
 			k8sObjects: []runtime.Object{
@@ -557,7 +542,6 @@ func TestCmdConnectorCreate_InputToOptions(t *testing.T) {
 		expectedSelector       string
 		expectedRoutingKey     string
 		expectedConnectorType  string
-		expectedOutput         string
 		expectedTimeout        time.Duration
 		expectedStatus         string
 	}
@@ -565,37 +549,34 @@ func TestCmdConnectorCreate_InputToOptions(t *testing.T) {
 	testTable := []test{
 		{
 			name:                   "test1",
-			flags:                  common.CommandConnectorCreateFlags{"backend", "", "app=backend", "secret", "tcp", true, "", 20 * time.Second, "json", "ready"},
+			flags:                  common.CommandConnectorCreateFlags{"backend", "", "app=backend", "secret", "tcp", true, "", 20 * time.Second, "ready"},
 			expectedTlsCredentials: "secret",
 			expectedHost:           "",
 			expectedRoutingKey:     "backend",
 			expectedTimeout:        20 * time.Second,
 			expectedConnectorType:  "tcp",
-			expectedOutput:         "json",
 			expectedSelector:       "app=backend",
 			expectedStatus:         "ready",
 		},
 		{
 			name:                   "test2",
-			flags:                  common.CommandConnectorCreateFlags{"backend", "backend", "", "secret", "tcp", true, "", 20 * time.Second, "json", "configured"},
+			flags:                  common.CommandConnectorCreateFlags{"backend", "backend", "", "secret", "tcp", true, "", 20 * time.Second, "configured"},
 			expectedTlsCredentials: "secret",
 			expectedHost:           "backend",
 			expectedRoutingKey:     "backend",
 			expectedTimeout:        20 * time.Second,
 			expectedConnectorType:  "tcp",
-			expectedOutput:         "json",
 			expectedSelector:       "",
 			expectedStatus:         "configured",
 		},
 		{
 			name:                   "test3",
-			flags:                  common.CommandConnectorCreateFlags{"", "", "", "secret", "tcp", false, "", 30 * time.Second, "yaml", "none"},
+			flags:                  common.CommandConnectorCreateFlags{"", "", "", "secret", "tcp", false, "", 30 * time.Second, "none"},
 			expectedTlsCredentials: "secret",
 			expectedHost:           "",
 			expectedRoutingKey:     "test3",
 			expectedTimeout:        30 * time.Second,
 			expectedConnectorType:  "tcp",
-			expectedOutput:         "yaml",
 			expectedSelector:       "app=test3",
 			expectedStatus:         "none",
 		},
@@ -613,7 +594,6 @@ func TestCmdConnectorCreate_InputToOptions(t *testing.T) {
 			cmd.InputToOptions()
 
 			assert.Check(t, cmd.routingKey == test.expectedRoutingKey)
-			assert.Check(t, cmd.output == test.expectedOutput)
 			assert.Check(t, cmd.tlsCredentials == test.expectedTlsCredentials)
 			assert.Check(t, cmd.host == test.expectedHost)
 			assert.Check(t, cmd.timeout == test.expectedTimeout)
@@ -650,20 +630,6 @@ func TestCmdConnectorCreate_Run(t *testing.T) {
 				Timeout:             10 * time.Second,
 			},
 		},
-		{
-			name:          "run output json",
-			connectorName: "my-connector-json",
-			connectorPort: 8080,
-			flags: common.CommandConnectorCreateFlags{
-				ConnectorType:       "tcp",
-				Host:                "hostname",
-				RoutingKey:          "keyname",
-				TlsCredentials:      "secretname",
-				IncludeNotReadyPods: true,
-				Timeout:             10 * time.Second,
-				Output:              "json",
-			},
-		},
 	}
 
 	for _, test := range testTable {
@@ -675,7 +641,6 @@ func TestCmdConnectorCreate_Run(t *testing.T) {
 			cmd.Flags = &common.CommandConnectorCreateFlags{}
 			cmd.name = test.connectorName
 			cmd.port = test.connectorPort
-			cmd.output = test.flags.Output
 			cmd.namespace = "test"
 
 			err := cmd.Run()
@@ -691,7 +656,6 @@ func TestCmdConnectorCreate_Run(t *testing.T) {
 func TestCmdConnectorCreate_WaitUntil(t *testing.T) {
 	type test struct {
 		name                string
-		output              string
 		status              string
 		k8sObjects          []runtime.Object
 		skupperObjects      []runtime.Object
@@ -740,29 +704,6 @@ func TestCmdConnectorCreate_WaitUntil(t *testing.T) {
 			},
 			expectError: false,
 		},
-		{
-			name:   "connector is ready yaml output",
-			output: "yaml",
-			skupperObjects: []runtime.Object{
-				&v2alpha1.Connector{
-					ObjectMeta: v1.ObjectMeta{
-						Name:      "my-connector",
-						Namespace: "test",
-					},
-					Status: v2alpha1.ConnectorStatus{
-						Status: v2alpha1.Status{
-							Conditions: []v1.Condition{
-								{
-									Type:   "Configured",
-									Status: "True",
-								},
-							},
-						},
-					},
-				},
-			},
-			expectError: false,
-		},
 		{
 			name:       "connector is not ready yet, but user waits for configured",
 			status:     "configured",
@@ -851,7 +792,6 @@ func TestCmdConnectorCreate_WaitUntil(t *testing.T) {
 		assert.Assert(t, err)
 
 		cmd.name = "my-connector"
-		cmd.output = test.output
 		cmd.timeout = 1 * time.Second
 		cmd.namespace = "test"
 		cmd.status = test.status
diff --git a/internal/cmd/skupper/connector/kube/connector_generate_test.go b/internal/cmd/skupper/connector/kube/connector_generate_test.go
new file mode 100644
index 00000000..f4462eff
--- /dev/null
+++ b/internal/cmd/skupper/connector/kube/connector_generate_test.go
@@ -0,0 +1,339 @@
+package kube
+
+import (
+	"testing"
+
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/testutils"
+
+	"gotest.tools/v3/assert"
+	"k8s.io/apimachinery/pkg/runtime"
+)
+
+func TestCmdConnectorGenerate_ValidateInput(t *testing.T) {
+	type test struct {
+		name           string
+		args           []string
+		flags          common.CommandConnectorGenerateFlags
+		k8sObjects     []runtime.Object
+		skupperObjects []runtime.Object
+		expectedError  string
+	}
+
+	testTable := []test{
+		{
+			name: "connector name and port are not specified",
+			args: []string{},
+			flags: common.CommandConnectorGenerateFlags{
+				Selector: "backend",
+			},
+			expectedError: "connector name and port must be configured",
+		},
+		{
+			name: "connector name empty",
+			args: []string{"", "8090"},
+			flags: common.CommandConnectorGenerateFlags{
+				Selector: "backend",
+			},
+			expectedError: "connector name must not be empty",
+		},
+		{
+			name: "connector port empty",
+			args: []string{"my-name-port-empty", ""},
+			flags: common.CommandConnectorGenerateFlags{
+				Selector: "backend",
+			},
+			expectedError: "connector port must not be empty",
+		},
+		{
+			name: "connector port not positive",
+			args: []string{"my-port-positive", "-45"},
+			flags: common.CommandConnectorGenerateFlags{
+				Selector: "backend",
+			},
+			expectedError: "connector port is not valid: value is not positive",
+		},
+		{
+			name: "connector name and port are not specified",
+			args: []string{},
+			flags: common.CommandConnectorGenerateFlags{
+				Selector: "backend",
+			},
+			expectedError: "connector name and port must be configured",
+		},
+		{
+			name: "connector port is not specified",
+			args: []string{"my-name"},
+			flags: common.CommandConnectorGenerateFlags{
+				Selector: "backend",
+			},
+			expectedError: "connector name and port must be configured",
+		},
+		{
+			name: "more than two arguments are specified",
+			args: []string{"my", "connector", "8080"},
+			flags: common.CommandConnectorGenerateFlags{
+				Selector: "backend",
+			},
+			expectedError: "only two arguments are allowed for this command",
+		},
+		{
+			name: "connector name is not valid.",
+			args: []string{"my new connector", "8080"},
+			flags: common.CommandConnectorGenerateFlags{
+				Selector: "backend",
+			},
+			expectedError: "connector name is not valid: value does not match this regular expression: ^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$",
+		},
+		{
+			name: "port is not valid.",
+			args: []string{"my-connector-port", "abcd"},
+			flags: common.CommandConnectorGenerateFlags{
+				Selector: "backend",
+			},
+			expectedError: "connector port is not valid: strconv.Atoi: parsing \"abcd\": invalid syntax",
+		},
+		{
+			name: "connector type is not valid",
+			args: []string{"my-connector-type", "8080"},
+			flags: common.CommandConnectorGenerateFlags{
+				ConnectorType: "not-valid",
+				Selector:      "backend",
+			},
+			expectedError: "connector type is not valid: value not-valid not allowed. It should be one of this options: [tcp]",
+		},
+		{
+			name: "routing key is not valid",
+			args: []string{"my-connector-rk", "8080"},
+			flags: common.CommandConnectorGenerateFlags{
+				RoutingKey: "not-valid$",
+				Selector:   "backend",
+			},
+			expectedError: "routing key is not valid: value does not match this regular expression: ^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$",
+		},
+		{
+			name: "tls-credentials does not exist",
+			args: []string{"my-connector-tls", "8080"},
+			flags: common.CommandConnectorGenerateFlags{
+				TlsCredentials: "not-$valid",
+				Selector:       "backend",
+			},
+			expectedError: "tlsCredentials is not valid: value does not match this regular expression: ^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$",
+		},
+		{
+			name: "workload is not valid",
+			args: []string{"bad-workload", "1234"},
+			flags: common.CommandConnectorGenerateFlags{
+				Workload: "@345",
+			},
+			expectedError: "workload is not valid: workload must include <resource-type>/<resource-name>",
+		},
+		{
+			name: "workload bad resourceType",
+			args: []string{"bad-workload", "1234"},
+			flags: common.CommandConnectorGenerateFlags{
+				Workload: "bad/backend",
+			},
+			expectedError: "workload is not valid: resource-type does not match expected value: deployment/service/daemonset/statefulset",
+		},
+		{
+			name: "selector is not valid",
+			args: []string{"bad-selector", "1234"},
+			flags: common.CommandConnectorGenerateFlags{
+				Selector: "@#$%",
+			},
+			expectedError: "selector is not valid: value does not match this regular expression: ^[A-Za-z0-9=:./-]+$",
+		},
+		{
+			name: "host is not valid",
+			args: []string{"my-connector-host", "8080"},
+			flags: common.CommandConnectorGenerateFlags{
+				Host: "not-valid$"},
+			expectedError: "host is not valid: a valid IP address or hostname is expected",
+		},
+		{
+			name: "output is not valid",
+			args: []string{"bad-output", "1234"},
+			flags: common.CommandConnectorGenerateFlags{
+				Host:   "host",
+				Output: "not-supported",
+			},
+			expectedError: "output type is not valid: value not-supported not allowed. It should be one of this options: [json yaml]",
+		},
+		{
+			name: "selector/host",
+			args: []string{"selector", "1234"},
+			flags: common.CommandConnectorGenerateFlags{
+				Output:   "json",
+				Selector: "app=test",
+				Host:     "test",
+			},
+			expectedError: "If host is configured, cannot configure workload or selector\n" +
+				"If selector is configured, cannot configure workload or host",
+		},
+		{
+			name: "workload/host",
+			args: []string{"workload", "1234"},
+			flags: common.CommandConnectorGenerateFlags{
+				Output:   "json",
+				Workload: "deployment/test",
+				Host:     "test",
+			},
+			expectedError: "If host is configured, cannot configure workload or selector\n" +
+				"If workload is configured, cannot configure selector or host",
+		},
+	}
+
+	for _, test := range testTable {
+		t.Run(test.name, func(t *testing.T) {
+
+			command, err := newCmdConnectorGenerateWithMocks("test", test.k8sObjects, test.skupperObjects, "")
+			assert.Assert(t, err)
+
+			command.Flags = &test.flags
+
+			testutils.CheckValidateInput(t, command, test.expectedError, test.args)
+
+		})
+	}
+}
+func TestCmdConnectorGenerate_InputToOptions(t *testing.T) {
+
+	type test struct {
+		name                   string
+		flags                  common.CommandConnectorGenerateFlags
+		Connectorname          string
+		expectedTlsCredentials string
+		expectedHost           string
+		expectedSelector       string
+		expectedRoutingKey     string
+		expectedConnectorType  string
+		expectedOutput         string
+	}
+
+	testTable := []test{
+		{
+			name:                   "test1",
+			flags:                  common.CommandConnectorGenerateFlags{"backend", "", "app=backend", "secret", "tcp", true, "", "json"},
+			expectedTlsCredentials: "secret",
+			expectedHost:           "",
+			expectedRoutingKey:     "backend",
+			expectedConnectorType:  "tcp",
+			expectedOutput:         "json",
+			expectedSelector:       "app=backend",
+		},
+		{
+			name:                   "test2",
+			flags:                  common.CommandConnectorGenerateFlags{"backend", "backend", "", "secret", "tcp", true, "", "json"},
+			expectedTlsCredentials: "secret",
+			expectedHost:           "backend",
+			expectedRoutingKey:     "backend",
+			expectedConnectorType:  "tcp",
+			expectedOutput:         "json",
+			expectedSelector:       "",
+		},
+		{
+			name:                   "test3",
+			flags:                  common.CommandConnectorGenerateFlags{"", "", "", "secret", "tcp", false, "", "yaml"},
+			expectedTlsCredentials: "secret",
+			expectedHost:           "",
+			expectedRoutingKey:     "test3",
+			expectedConnectorType:  "tcp",
+			expectedOutput:         "yaml",
+			expectedSelector:       "app=test3",
+		},
+	}
+
+	for _, test := range testTable {
+		t.Run(test.name, func(t *testing.T) {
+
+			cmd, err := newCmdConnectorGenerateWithMocks("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.tlsCredentials == test.expectedTlsCredentials)
+			assert.Check(t, cmd.host == test.expectedHost)
+			assert.Check(t, cmd.selector == test.expectedSelector)
+			assert.Check(t, cmd.connectorType == test.expectedConnectorType)
+		})
+	}
+}
+
+func TestCmdConnectorGenerate_Run(t *testing.T) {
+	type test struct {
+		name                string
+		connectorName       string
+		connectorPort       int
+		flags               common.CommandConnectorGenerateFlags
+		k8sObjects          []runtime.Object
+		skupperObjects      []runtime.Object
+		skupperErrorMessage string
+		errorMessage        string
+	}
+
+	testTable := []test{
+		{
+			name:          "runs ok yaml",
+			connectorName: "my-connector-ok",
+			connectorPort: 8080,
+			flags: common.CommandConnectorGenerateFlags{
+				ConnectorType:       "tcp",
+				RoutingKey:          "keyname",
+				TlsCredentials:      "secretname",
+				IncludeNotReadyPods: true,
+				Selector:            "app=backend",
+				Output:              "yaml",
+			},
+		},
+		{
+			name:          "run ok json",
+			connectorName: "my-connector-json",
+			connectorPort: 8080,
+			flags: common.CommandConnectorGenerateFlags{
+				ConnectorType:       "tcp",
+				Host:                "hostname",
+				RoutingKey:          "keyname",
+				TlsCredentials:      "secretname",
+				IncludeNotReadyPods: true,
+				Output:              "json",
+			},
+		},
+	}
+
+	for _, test := range testTable {
+		cmd, err := newCmdConnectorGenerateWithMocks("test", test.k8sObjects, test.skupperObjects, test.skupperErrorMessage)
+		assert.Assert(t, err)
+
+		t.Run(test.name, func(t *testing.T) {
+
+			cmd.Flags = &common.CommandConnectorGenerateFlags{}
+			cmd.name = test.connectorName
+			cmd.port = test.connectorPort
+			cmd.output = test.flags.Output
+			cmd.namespace = "test"
+
+			err := cmd.Run()
+			if err != nil {
+				assert.Check(t, test.errorMessage == err.Error())
+			} else {
+				assert.Check(t, err == nil)
+			}
+		})
+	}
+}
+
+// --- helper methods
+
+func newCmdConnectorGenerateWithMocks(namespace string, k8sObjects []runtime.Object, skupperObjects []runtime.Object, fakeSkupperError string) (*CmdConnectorGenerate, error) {
+
+	cmdConnectorGenerate := &CmdConnectorGenerate{
+		namespace: namespace,
+	}
+	return cmdConnectorGenerate, nil
+}
diff --git a/internal/cmd/skupper/connector/kube/connector_update_test.go b/internal/cmd/skupper/connector/kube/connector_update_test.go
index 0622de96..d08dc318 100644
--- a/internal/cmd/skupper/connector/kube/connector_update_test.go
+++ b/internal/cmd/skupper/connector/kube/connector_update_test.go
@@ -225,7 +225,6 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 			args: []string{"selector"},
 			flags: common.CommandConnectorUpdateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Selector: "app=test",
 				Host:     "test",
 			},
@@ -255,7 +254,6 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 			args: []string{"workload"},
 			flags: common.CommandConnectorUpdateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "deployment/test",
 				Host:     "test",
 			},
@@ -326,33 +324,6 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 			},
 			expectedError: "timeout is not valid: duration must not be less than 10s; got 1s",
 		},
-		{
-			name: "output is not valid",
-			args: []string{"bad-output"},
-			flags: common.CommandConnectorUpdateFlags{
-				Output:  "not-supported",
-				Timeout: 10 * time.Minute,
-			},
-			skupperObjects: []runtime.Object{
-				&v2alpha1.Connector{
-					ObjectMeta: v1.ObjectMeta{
-						Name:      "bad-output",
-						Namespace: "test",
-					},
-					Status: v2alpha1.ConnectorStatus{
-						Status: v2alpha1.Status{
-							Conditions: []v1.Condition{
-								{
-									Type:   "Configured",
-									Status: "True",
-								},
-							},
-						},
-					},
-				},
-			},
-			expectedError: "output type is not valid: value not-supported not allowed. It should be one of this options: [json yaml]",
-		},
 		{
 			name: "flags all valid",
 			args: []string{"my-connector-flags"},
@@ -363,7 +334,6 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 				ConnectorType:       "tcp",
 				IncludeNotReadyPods: false,
 				Timeout:             50 * time.Second,
-				Output:              "json",
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.Connector{
@@ -449,7 +419,6 @@ func TestCmdConnectorUpdate_ValidateWorkload(t *testing.T) {
 			name: "workload-no-deployment",
 			args: []string{"workload-deployment"}, flags: common.CommandConnectorUpdateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "deployment/backend",
 			},
 			skupperObjects: []runtime.Object{
@@ -476,7 +445,6 @@ func TestCmdConnectorUpdate_ValidateWorkload(t *testing.T) {
 			name: "workload-deployment-no-labels",
 			args: []string{"workload-deployment-no-labels"}, flags: common.CommandConnectorUpdateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "deployment/backend",
 			},
 			skupperObjects: []runtime.Object{
@@ -518,7 +486,6 @@ func TestCmdConnectorUpdate_ValidateWorkload(t *testing.T) {
 			name: "workload-deployment",
 			args: []string{"workload-deployment"}, flags: common.CommandConnectorUpdateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "deployment/backend",
 			},
 			skupperObjects: []runtime.Object{
@@ -565,7 +532,6 @@ func TestCmdConnectorUpdate_ValidateWorkload(t *testing.T) {
 			name: "workload-no-service",
 			args: []string{"workload-no-service"}, flags: common.CommandConnectorUpdateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "service/backend",
 			},
 			skupperObjects: []runtime.Object{
@@ -592,7 +558,6 @@ func TestCmdConnectorUpdate_ValidateWorkload(t *testing.T) {
 			name: "workload-service-no-labels",
 			args: []string{"workload-service-no-labels"}, flags: common.CommandConnectorUpdateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "service/backend",
 			},
 			skupperObjects: []runtime.Object{
@@ -632,7 +597,6 @@ func TestCmdConnectorUpdate_ValidateWorkload(t *testing.T) {
 			name: "workload-service",
 			args: []string{"workload-service"}, flags: common.CommandConnectorUpdateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "service/backend",
 			},
 			skupperObjects: []runtime.Object{
@@ -677,7 +641,6 @@ func TestCmdConnectorUpdate_ValidateWorkload(t *testing.T) {
 			name: "workload-no-daemonset",
 			args: []string{"workload-no-daemonset"}, flags: common.CommandConnectorUpdateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "daemonset/backend",
 			},
 			skupperObjects: []runtime.Object{
@@ -704,7 +667,6 @@ func TestCmdConnectorUpdate_ValidateWorkload(t *testing.T) {
 			name: "workload-daemonset-no-labels",
 			args: []string{"workload-daemonset-no-labels"}, flags: common.CommandConnectorUpdateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "daemonset/backend",
 			},
 			skupperObjects: []runtime.Object{
@@ -746,7 +708,6 @@ func TestCmdConnectorUpdate_ValidateWorkload(t *testing.T) {
 			name: "workload-daemonset",
 			args: []string{"workload-daemonset"}, flags: common.CommandConnectorUpdateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "DaemonSet/backend",
 			},
 			skupperObjects: []runtime.Object{
@@ -793,7 +754,6 @@ func TestCmdConnectorUpdate_ValidateWorkload(t *testing.T) {
 			name: "workload-no-statefulset",
 			args: []string{"workload-no-statefulset"}, flags: common.CommandConnectorUpdateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "StatefulSet/backend",
 			},
 			skupperObjects: []runtime.Object{
@@ -820,7 +780,6 @@ func TestCmdConnectorUpdate_ValidateWorkload(t *testing.T) {
 			name: "workload-statefulset-no-labels",
 			args: []string{"workload-statefulset-no-labels"}, flags: common.CommandConnectorUpdateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "statefulset/backend",
 			},
 			skupperObjects: []runtime.Object{
@@ -862,7 +821,6 @@ func TestCmdConnectorUpdate_ValidateWorkload(t *testing.T) {
 			name: "workload-statefulset",
 			args: []string{"workload-statefulset"}, flags: common.CommandConnectorUpdateFlags{
 				Timeout:  10 * time.Second,
-				Output:   "json",
 				Workload: "statefulset/backend",
 			},
 			skupperObjects: []runtime.Object{
@@ -968,40 +926,6 @@ func TestCmdConnectorUpdate_Run(t *testing.T) {
 				},
 			},
 		},
-		{
-			name:          "run output json",
-			connectorName: "my-connector-json",
-			flags: ConnectorUpdates{
-				port:                8181,
-				connectorType:       "tcp",
-				host:                "hostname",
-				routingKey:          "keyname",
-				tlsCredentials:      "secretname",
-				includeNotReadyPods: true,
-				workload:            "deployment/backend",
-				selector:            "backend",
-				timeout:             10 * time.Second,
-				output:              "json",
-			},
-			skupperObjects: []runtime.Object{
-				&v2alpha1.Connector{
-					ObjectMeta: v1.ObjectMeta{
-						Name:      "my-connector-json",
-						Namespace: "test",
-					},
-					Status: v2alpha1.ConnectorStatus{
-						Status: v2alpha1.Status{
-							Conditions: []v1.Condition{
-								{
-									Type:   "Configured",
-									Status: "True",
-								},
-							},
-						},
-					},
-				},
-			},
-		},
 	}
 
 	for _, test := range testTable {
@@ -1027,7 +951,6 @@ func TestCmdConnectorUpdate_Run(t *testing.T) {
 func TestCmdConnectorUpdate_WaitUntil(t *testing.T) {
 	type test struct {
 		name                string
-		output              string
 		status              string
 		k8sObjects          []runtime.Object
 		skupperObjects      []runtime.Object
@@ -1076,29 +999,6 @@ func TestCmdConnectorUpdate_WaitUntil(t *testing.T) {
 			},
 			expectError: false,
 		},
-		{
-			name:   "connector is ready json output",
-			output: "json",
-			skupperObjects: []runtime.Object{
-				&v2alpha1.Connector{
-					ObjectMeta: v1.ObjectMeta{
-						Name:      "my-connector",
-						Namespace: "test",
-					},
-					Status: v2alpha1.ConnectorStatus{
-						Status: v2alpha1.Status{
-							Conditions: []v1.Condition{
-								{
-									Type:   "Configured",
-									Status: "True",
-								},
-							},
-						},
-					},
-				},
-			},
-			expectError: false,
-		},
 		{
 			name:       "connector is not ready yet, but user waits for configured",
 			status:     "configured",
@@ -1189,10 +1089,8 @@ func TestCmdConnectorUpdate_WaitUntil(t *testing.T) {
 		cmd.name = "my-connector"
 		cmd.Flags = &common.CommandConnectorUpdateFlags{
 			Timeout: 10 * time.Second,
-			Output:  test.output,
 		}
 		cmd.namespace = "test"
-		cmd.newSettings.output = cmd.Flags.Output
 		cmd.status = test.status
 
 		t.Run(test.name, func(t *testing.T) {
diff --git a/internal/cmd/skupper/connector/nonkube/connector_create_test.go b/internal/cmd/skupper/connector/nonkube/connector_create_test.go
index 7479d1b4..397ce22a 100644
--- a/internal/cmd/skupper/connector/nonkube/connector_create_test.go
+++ b/internal/cmd/skupper/connector/nonkube/connector_create_test.go
@@ -97,16 +97,10 @@ func TestNonKubeCmdConnectorCreate_ValidateInput(t *testing.T) {
 			expectedError: "host name must be configured: an IP address or hostname is expected",
 		},
 		{
-			name:          "output format is not valid",
-			args:          []string{"my-connector", "8080"},
-			flags:         &common.CommandConnectorCreateFlags{Output: "not-valid", Host: "1.2.3.4"},
-			expectedError: "output type is not valid: value not-valid not allowed. It should be one of this options: [json yaml]",
-		},
-		{
-			name:          "kubernetes flags are not valid on this platform",
-			args:          []string{"my-connector", "8080"},
-			flags:         &common.CommandConnectorCreateFlags{Host: "1.2.3.4"},
-			expectedError: "",
+
+			name:  "kubernetes flags are not valid on this platform",
+			args:  []string{"my-connector", "8080"},
+			flags: &common.CommandConnectorCreateFlags{Host: "1.2.3.4"},
 			cobraGenericFlags: map[string]string{
 				common.FlagNameContext:    "test",
 				common.FlagNameKubeconfig: "test",
@@ -119,7 +113,6 @@ func TestNonKubeCmdConnectorCreate_ValidateInput(t *testing.T) {
 				RoutingKey:     "routingkeyname",
 				TlsCredentials: "secretname",
 				ConnectorType:  "tcp",
-				Output:         "json",
 				Host:           "1.2.3.4",
 			},
 			expectedError: "",
@@ -159,40 +152,36 @@ func TestNonKubeCmdConnectorCreate_InputToOptions(t *testing.T) {
 		expectedHost           string
 		expectedRoutingKey     string
 		expectedConnectorType  string
-		expectedOutput         string
 	}
 
 	testTable := []test{
 		{
 			name:                   "test1",
-			flags:                  common.CommandConnectorCreateFlags{"backend", "", "", "secret", "tcp", false, "", 0, "json", "none"},
+			flags:                  common.CommandConnectorCreateFlags{"backend", "", "", "secret", "tcp", false, "", 0, "none"},
 			expectedTlsCredentials: "secret",
 			expectedHost:           "",
 			expectedRoutingKey:     "backend",
 			expectedConnectorType:  "tcp",
-			expectedOutput:         "json",
 			expectedNamespace:      "default",
 		},
 		{
 			name:                   "test2",
 			namespace:              "test",
-			flags:                  common.CommandConnectorCreateFlags{"backend", "1.2.3.4", "", "secret", "tcp", false, "", 0, "json", "configured"},
+			flags:                  common.CommandConnectorCreateFlags{"backend", "1.2.3.4", "", "secret", "tcp", false, "", 0, "configured"},
 			expectedTlsCredentials: "secret",
 			expectedHost:           "1.2.3.4",
 			expectedRoutingKey:     "backend",
 			expectedConnectorType:  "tcp",
-			expectedOutput:         "json",
 			expectedNamespace:      "test",
 		},
 		{
 			name:                   "test3",
 			namespace:              "test",
-			flags:                  common.CommandConnectorCreateFlags{"", "", "", "secret", "tcp", false, "", 0, "yaml", "ready"},
+			flags:                  common.CommandConnectorCreateFlags{"", "", "", "secret", "tcp", false, "", 0, "ready"},
 			expectedTlsCredentials: "secret",
 			expectedHost:           "",
 			expectedRoutingKey:     "my-Connector",
 			expectedConnectorType:  "tcp",
-			expectedOutput:         "yaml",
 			expectedNamespace:      "test",
 		},
 	}
@@ -211,7 +200,6 @@ func TestNonKubeCmdConnectorCreate_InputToOptions(t *testing.T) {
 			assert.Check(t, cmd.tlsCredentials == test.expectedTlsCredentials)
 			assert.Check(t, cmd.host == test.expectedHost)
 			assert.Check(t, cmd.connectorType == test.expectedConnectorType)
-			assert.Check(t, cmd.output == test.expectedOutput)
 			assert.Check(t, cmd.namespace == test.expectedNamespace)
 		})
 	}
@@ -226,7 +214,6 @@ func TestNonKubeCmdConnectorCreate_Run(t *testing.T) {
 		skupperError   string
 		connectorName  string
 		host           string
-		output         string
 		errorMessage   string
 		routingKey     string
 		tlsCredentials string
@@ -247,34 +234,12 @@ func TestNonKubeCmdConnectorCreate_Run(t *testing.T) {
 			tlsCredentials: "secretname",
 			host:           "1.2.3.4",
 		},
-		{
-			name:           "runs ok yaml",
-			k8sObjects:     nil,
-			skupperObjects: nil,
-			connectorName:  "test2",
-			connectorPort:  8080,
-			connectorType:  "tcp",
-			host:           "2.2.2.2",
-			output:         "yaml",
-		},
-		{
-			name:           "runs fails because the output format is not supported",
-			namespace:      "default",
-			k8sObjects:     nil,
-			skupperObjects: nil,
-			connectorName:  "test3",
-			connectorPort:  8080,
-			host:           "3.3.3.3",
-			output:         "unsupported",
-			errorMessage:   "format unsupported not supported",
-		},
 	}
 
 	for _, test := range testTable {
 		command := &CmdConnectorCreate{}
 
 		command.connectorName = test.connectorName
-		command.output = test.output
 		command.port = test.connectorPort
 		command.host = test.host
 		command.connectorType = test.connectorType
diff --git a/internal/cmd/skupper/connector/nonkube/connector_generate_test.go b/internal/cmd/skupper/connector/nonkube/connector_generate_test.go
new file mode 100644
index 00000000..4aea3f5b
--- /dev/null
+++ b/internal/cmd/skupper/connector/nonkube/connector_generate_test.go
@@ -0,0 +1,290 @@
+package nonkube
+
+import (
+	"testing"
+
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/testutils"
+	"github.com/skupperproject/skupper/internal/nonkube/client/fs"
+	"github.com/spf13/cobra"
+
+	"gotest.tools/v3/assert"
+	"k8s.io/apimachinery/pkg/runtime"
+)
+
+func TestNonKubeCmdConnectorGenerate_ValidateInput(t *testing.T) {
+	type test struct {
+		name              string
+		args              []string
+		k8sObjects        []runtime.Object
+		skupperObjects    []runtime.Object
+		flags             *common.CommandConnectorGenerateFlags
+		cobraGenericFlags map[string]string
+		expectedError     string
+	}
+
+	testTable := []test{
+		{
+			name:          "Connector name and port are not specified",
+			args:          []string{},
+			flags:         &common.CommandConnectorGenerateFlags{Host: "1.2.3.4"},
+			expectedError: "connector name and port must be configured",
+		},
+		{
+			name:          "Connector name is not valid",
+			args:          []string{"my new Connector", "8080"},
+			flags:         &common.CommandConnectorGenerateFlags{Host: "1.2.3.4"},
+			expectedError: "connector name is not valid: value does not match this regular expression: ^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$",
+		},
+		{
+			name:          "Connector name is empty",
+			args:          []string{"", "1234"},
+			flags:         &common.CommandConnectorGenerateFlags{Host: "1.2.3.4"},
+			expectedError: "connector name must not be empty",
+		},
+		{
+			name:          "connector port empty",
+			args:          []string{"my-name-port-empty", ""},
+			flags:         &common.CommandConnectorGenerateFlags{Host: "1.2.3.4"},
+			expectedError: "connector port must not be empty",
+		},
+		{
+			name:          "port is not valid",
+			args:          []string{"my-connector-port", "abcd"},
+			flags:         &common.CommandConnectorGenerateFlags{Host: "1.2.3.4"},
+			expectedError: "connector port is not valid: strconv.Atoi: parsing \"abcd\": invalid syntax",
+		},
+		{
+			name:          "port not positive",
+			args:          []string{"my-port-positive", "-45"},
+			flags:         &common.CommandConnectorGenerateFlags{Host: "1.2.3.4"},
+			expectedError: "connector port is not valid: value is not positive",
+		},
+		{
+			name:          "more than two arguments was specified",
+			args:          []string{"my", "Connector", "test"},
+			flags:         &common.CommandConnectorGenerateFlags{Host: "1.2.3.4"},
+			expectedError: "only two arguments are allowed for this command",
+		},
+		{
+			name:          "type is not valid",
+			args:          []string{"my-connector", "8080"},
+			flags:         &common.CommandConnectorGenerateFlags{ConnectorType: "not-valid", Host: "1.2.3.4"},
+			expectedError: "connector type is not valid: value not-valid not allowed. It should be one of this options: [tcp]",
+		},
+		{
+			name:          "routing key is not valid",
+			args:          []string{"my-connector-rk", "8080"},
+			flags:         &common.CommandConnectorGenerateFlags{RoutingKey: "not-valid$", Host: "1.2.3.4"},
+			expectedError: "routing key is not valid: value does not match this regular expression: ^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$",
+		},
+		{
+			name:          "host is not valid",
+			args:          []string{"my-connector-host", "8080"},
+			flags:         &common.CommandConnectorGenerateFlags{Host: "not-valid$"},
+			expectedError: "host is not valid: a valid IP address or hostname is expected",
+		},
+		{
+			name:          "host is not configued",
+			args:          []string{"my-connector-host", "8080"},
+			flags:         &common.CommandConnectorGenerateFlags{},
+			expectedError: "host name must be configured: an IP address or hostname is expected",
+		},
+		{
+			name:          "tlsCredentials is not valid",
+			args:          []string{"my-connector-tls", "8080"},
+			flags:         &common.CommandConnectorGenerateFlags{TlsCredentials: "not-valid$", Host: "1.2.3.4"},
+			expectedError: "tlsCredentials is not valid: value does not match this regular expression: ^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$",
+		},
+		{
+			name:          "output format is not valid",
+			args:          []string{"my-connector", "8080"},
+			flags:         &common.CommandConnectorGenerateFlags{Output: "not-valid", Host: "1.2.3.4"},
+			expectedError: "output type is not valid: value not-valid not allowed. It should be one of this options: [json yaml]",
+		},
+		{
+			name:  "kubernetes flags are not valid on this platform",
+			args:  []string{"my-connector", "8080"},
+			flags: &common.CommandConnectorGenerateFlags{Host: "1.2.3.4"},
+			cobraGenericFlags: map[string]string{
+				common.FlagNameContext:    "test",
+				common.FlagNameKubeconfig: "test",
+			},
+		},
+		{
+			name: "flags all valid",
+			args: []string{"my-connector-flags", "8080"},
+			flags: &common.CommandConnectorGenerateFlags{
+				RoutingKey:     "routingkeyname",
+				TlsCredentials: "secretname",
+				ConnectorType:  "tcp",
+				Output:         "json",
+				Host:           "1.2.3.4",
+			},
+		},
+	}
+
+	for _, test := range testTable {
+		t.Run(test.name, func(t *testing.T) {
+			command := &CmdConnectorGenerate{Flags: &common.CommandConnectorGenerateFlags{}}
+			command.CobraCmd = &cobra.Command{Use: "test"}
+
+			if test.flags != nil {
+				command.Flags = test.flags
+			}
+
+			if test.cobraGenericFlags != nil && len(test.cobraGenericFlags) > 0 {
+				for name, value := range test.cobraGenericFlags {
+					command.CobraCmd.Flags().String(name, value, "")
+				}
+			}
+
+			testutils.CheckValidateInput(t, command, test.expectedError, test.args)
+		})
+	}
+}
+
+func TestNonKubeCmdConnectorGenerate_InputToOptions(t *testing.T) {
+
+	type test struct {
+		name                   string
+		args                   []string
+		namespace              string
+		flags                  common.CommandConnectorGenerateFlags
+		expectedNamespace      string
+		Connectorname          string
+		expectedTlsCredentials string
+		expectedHost           string
+		expectedRoutingKey     string
+		expectedConnectorType  string
+		expectedOutput         string
+	}
+
+	testTable := []test{
+		{
+			name:                   "test1",
+			flags:                  common.CommandConnectorGenerateFlags{"backend", "", "", "secret", "tcp", false, "", "json"},
+			expectedTlsCredentials: "secret",
+			expectedHost:           "",
+			expectedRoutingKey:     "backend",
+			expectedConnectorType:  "tcp",
+			expectedOutput:         "json",
+			expectedNamespace:      "default",
+		},
+		{
+			name:                  "test2",
+			namespace:             "test",
+			flags:                 common.CommandConnectorGenerateFlags{"backend", "1.2.3.4", "", "", "tcp", false, "", "yaml"},
+			expectedHost:          "1.2.3.4",
+			expectedRoutingKey:    "backend",
+			expectedConnectorType: "tcp",
+			expectedOutput:        "yaml",
+			expectedNamespace:     "test",
+		},
+		{
+			name:                   "test3",
+			namespace:              "test",
+			flags:                  common.CommandConnectorGenerateFlags{"", "", "", "secret", "tcp", false, "", "yaml"},
+			expectedTlsCredentials: "secret",
+			expectedHost:           "",
+			expectedRoutingKey:     "my-Connector",
+			expectedConnectorType:  "tcp",
+			expectedOutput:         "yaml",
+			expectedNamespace:      "test",
+		},
+	}
+
+	for _, test := range testTable {
+		t.Run(test.name, func(t *testing.T) {
+			cmd := CmdConnectorGenerate{}
+			cmd.Flags = &test.flags
+			cmd.connectorName = "my-Connector"
+			cmd.namespace = test.namespace
+			cmd.connectorHandler = fs.NewConnectorHandler(cmd.namespace)
+
+			cmd.InputToOptions()
+
+			assert.Check(t, cmd.routingKey == test.expectedRoutingKey)
+			assert.Check(t, cmd.tlsCredentials == test.expectedTlsCredentials)
+			assert.Check(t, cmd.host == test.expectedHost)
+			assert.Check(t, cmd.connectorType == test.expectedConnectorType)
+			assert.Check(t, cmd.output == test.expectedOutput)
+			assert.Check(t, cmd.namespace == test.expectedNamespace)
+		})
+	}
+}
+
+func TestNonKubeCmdConnectorGenerate_Run(t *testing.T) {
+	type test struct {
+		name           string
+		namespace      string
+		k8sObjects     []runtime.Object
+		skupperObjects []runtime.Object
+		skupperError   string
+		connectorName  string
+		host           string
+		output         string
+		errorMessage   string
+		routingKey     string
+		tlsCredentials string
+		connectorType  string
+		connectorPort  int
+	}
+
+	testTable := []test{
+		{
+			name:           "runs ok",
+			namespace:      "test",
+			k8sObjects:     nil,
+			skupperObjects: nil,
+			connectorName:  "test1",
+			connectorPort:  8080,
+			connectorType:  "tcp",
+			routingKey:     "keyname",
+			tlsCredentials: "secretname",
+			host:           "1.2.3.4",
+			output:         "json",
+		},
+		{
+			name:           "runs ok yaml",
+			k8sObjects:     nil,
+			skupperObjects: nil,
+			connectorName:  "test2",
+			connectorPort:  8080,
+			connectorType:  "tcp",
+			host:           "2.2.2.2",
+			output:         "yaml",
+		},
+		{
+			name:           "runs fails because the output format is not supported",
+			namespace:      "default",
+			k8sObjects:     nil,
+			skupperObjects: nil,
+			connectorName:  "test3",
+			connectorPort:  8080,
+			host:           "3.3.3.3",
+			output:         "unsupported",
+			errorMessage:   "format unsupported not supported",
+		},
+	}
+
+	for _, test := range testTable {
+		command := &CmdConnectorGenerate{}
+
+		command.connectorName = test.connectorName
+		command.output = test.output
+		command.port = test.connectorPort
+		command.host = test.host
+		command.connectorType = test.connectorType
+		command.namespace = test.namespace
+		t.Run(test.name, func(t *testing.T) {
+
+			err := command.Run()
+			if err != nil {
+				assert.Check(t, test.errorMessage == err.Error(), err.Error())
+			} else {
+				assert.Check(t, err == nil)
+			}
+		})
+	}
+}
diff --git a/internal/cmd/skupper/connector/nonkube/connector_update_test.go b/internal/cmd/skupper/connector/nonkube/connector_update_test.go
index 9f4e1be0..67e606d0 100644
--- a/internal/cmd/skupper/connector/nonkube/connector_update_test.go
+++ b/internal/cmd/skupper/connector/nonkube/connector_update_test.go
@@ -91,12 +91,6 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 			flags:         &common.CommandConnectorUpdateFlags{Port: -1},
 			expectedError: "connector port is not valid: value is not positive",
 		},
-		{
-			name:          "output is not valid",
-			args:          []string{"my-connector"},
-			flags:         &common.CommandConnectorUpdateFlags{Output: "not-supported"},
-			expectedError: "output type is not valid: value not-supported not allowed. It should be one of this options: [json yaml]",
-		},
 		{
 			name:  "kubernetes flags are not valid on this platform",
 			args:  []string{"my-connector"},
@@ -115,7 +109,6 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 				TlsCredentials: "secretname",
 				Port:           1234,
 				ConnectorType:  "tcp",
-				Output:         "json",
 				Host:           "1.2.3.4",
 			},
 			expectedError: "",
@@ -173,7 +166,6 @@ func TestCmdConnectorUpdate_Run(t *testing.T) {
 		errorMessage        string
 		connectorName       string
 		host                string
-		output              string
 		routingKey          string
 		tlsCredentials      string
 		connectorType       string
@@ -192,14 +184,13 @@ func TestCmdConnectorUpdate_Run(t *testing.T) {
 			tlsCredentials: "secretname",
 		},
 		{
-			name:           "run output json",
+			name:           "runs default namespace",
 			connectorName:  "my-connector",
-			port:           8181,
+			port:           8080,
 			connectorType:  "tcp",
 			host:           "hostname",
 			routingKey:     "keyname",
 			tlsCredentials: "secretname",
-			output:         "json",
 		},
 	}
 
@@ -207,7 +198,6 @@ func TestCmdConnectorUpdate_Run(t *testing.T) {
 		command := &CmdConnectorUpdate{}
 
 		command.connectorName = test.connectorName
-		command.output = test.output
 		command.newSettings.port = test.port
 		command.newSettings.host = test.host
 		command.newSettings.routingKey = test.routingKey
diff --git a/internal/cmd/skupper/link/kube/link_update_test.go b/internal/cmd/skupper/link/kube/link_update_test.go
index a837f9f7..8a5e857b 100644
--- a/internal/cmd/skupper/link/kube/link_update_test.go
+++ b/internal/cmd/skupper/link/kube/link_update_test.go
@@ -178,30 +178,6 @@ func TestCmdLinkUpdate_ValidateInput(t *testing.T) {
 			},
 			expectedError: "link cost is not valid: value is not positive",
 		},
-		{
-			name:  "output format is not valid",
-			args:  []string{"my-link"},
-			flags: common.CommandLinkUpdateFlags{Cost: "1", Output: "not-valid", Timeout: time.Minute},
-			skupperObjects: []runtime.Object{
-				&v2alpha1.SiteList{
-					Items: []v2alpha1.Site{
-						{
-							ObjectMeta: v1.ObjectMeta{
-								Name:      "the-site",
-								Namespace: "test",
-							},
-						},
-					},
-				},
-				&v2alpha1.Link{
-					ObjectMeta: v1.ObjectMeta{
-						Name:      "my-link",
-						Namespace: "test",
-					},
-				},
-			},
-			expectedError: "output type is not valid: value not-valid not allowed. It should be one of this options: [json yaml]",
-		},
 		{
 			name:  "tls secret not available",
 			args:  []string{"my-link"},
@@ -306,7 +282,6 @@ func TestCmdLinkUpdate_InputToOptions(t *testing.T) {
 		flags                  common.CommandLinkUpdateFlags
 		expectedTlsCredentials string
 		expectedCost           int
-		expectedOutput         string
 		expectedTimeout        time.Duration
 		expectedStatus         string
 	}
@@ -315,10 +290,9 @@ func TestCmdLinkUpdate_InputToOptions(t *testing.T) {
 		{
 			name:                   "check options",
 			args:                   []string{"my-link"},
-			flags:                  common.CommandLinkUpdateFlags{TlsCredentials: "secret", Cost: "1", Output: "json", Timeout: time.Minute, Wait: "ready"},
+			flags:                  common.CommandLinkUpdateFlags{TlsCredentials: "secret", Cost: "1", Timeout: time.Minute, Wait: "ready"},
 			expectedCost:           1,
 			expectedTlsCredentials: "secret",
-			expectedOutput:         "json",
 			expectedTimeout:        time.Minute,
 			expectedStatus:         "ready",
 		},
@@ -333,7 +307,6 @@ func TestCmdLinkUpdate_InputToOptions(t *testing.T) {
 
 			cmd.InputToOptions()
 
-			assert.Check(t, cmd.output == test.expectedOutput)
 			assert.Check(t, cmd.tlsCredentials == test.expectedTlsCredentials)
 			assert.Check(t, cmd.cost == test.expectedCost)
 			assert.Check(t, cmd.timeout == test.expectedTimeout)
@@ -349,7 +322,6 @@ func TestCmdLinkUpdate_Run(t *testing.T) {
 		skupperObjects      []runtime.Object
 		linkName            string
 		Cost                int
-		output              string
 		tlsCredentials      string
 		errorMessage        string
 		skupperErrorMessage string
@@ -384,21 +356,6 @@ func TestCmdLinkUpdate_Run(t *testing.T) {
 		{
 			name:     "runs ok without updating link",
 			linkName: "my-link",
-			output:   "yaml",
-			skupperObjects: []runtime.Object{
-				&v2alpha1.Link{
-					ObjectMeta: v1.ObjectMeta{
-						Name:      "my-link",
-						Namespace: "test",
-					},
-				},
-			},
-		},
-		{
-			name:         "runs fails because the output format is not supported",
-			linkName:     "my-link",
-			output:       "unsupported",
-			errorMessage: "format unsupported not supported",
 			skupperObjects: []runtime.Object{
 				&v2alpha1.Link{
 					ObjectMeta: v1.ObjectMeta{
@@ -415,7 +372,6 @@ func TestCmdLinkUpdate_Run(t *testing.T) {
 		assert.Assert(t, err)
 
 		cmd.linkName = test.linkName
-		cmd.output = test.output
 		cmd.tlsCredentials = test.tlsCredentials
 		cmd.cost = test.Cost
 
@@ -439,7 +395,6 @@ func TestCmdLinkUpdate_WaitUntil(t *testing.T) {
 		skupperObjects      []runtime.Object
 		skupperErrorMessage string
 		linkName            string
-		output              string
 		timeout             time.Duration
 		expectError         bool
 	}
@@ -467,13 +422,6 @@ func TestCmdLinkUpdate_WaitUntil(t *testing.T) {
 			timeout:     time.Second,
 			expectError: true,
 		},
-		{
-			name:        "there is no need to wait for a link, the user just wanted the output",
-			linkName:    "my-link",
-			output:      "json",
-			timeout:     time.Second,
-			expectError: false,
-		},
 		{
 			name:   "link is ready",
 			status: "ready",
@@ -599,7 +547,6 @@ func TestCmdLinkUpdate_WaitUntil(t *testing.T) {
 		cmd, err := newCmdLinkUpdateWithMocks("test", test.k8sObjects, test.skupperObjects, test.skupperErrorMessage)
 		assert.Assert(t, err)
 		cmd.linkName = test.linkName
-		cmd.output = test.output
 		cmd.timeout = test.timeout
 		cmd.status = test.status
 
diff --git a/internal/cmd/skupper/link/link_test.go b/internal/cmd/skupper/link/link_test.go
index b0889d6b..03af366c 100644
--- a/internal/cmd/skupper/link/link_test.go
+++ b/internal/cmd/skupper/link/link_test.go
@@ -2,12 +2,13 @@ package link
 
 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/v3/assert"
-	"testing"
 )
 
 func TestCmdLinkFactory(t *testing.T) {
@@ -35,7 +36,6 @@ func TestCmdLinkFactory(t *testing.T) {
 			expectedFlagsWithDefaultValue: map[string]interface{}{
 				common.FlagNameTlsCredentials: "",
 				common.FlagNameCost:           "1",
-				common.FlagNameOutput:         "",
 				common.FlagNameTimeout:        "1m0s",
 				common.FlagNameWait:           "ready",
 			},
diff --git a/internal/cmd/skupper/listener/kube/listener_create_test.go b/internal/cmd/skupper/listener/kube/listener_create_test.go
index 3d7f8a89..750530d4 100644
--- a/internal/cmd/skupper/listener/kube/listener_create_test.go
+++ b/internal/cmd/skupper/listener/kube/listener_create_test.go
@@ -140,15 +140,6 @@ func TestCmdListenerCreate_ValidateInput(t *testing.T) {
 			flags:         common.CommandListenerCreateFlags{Timeout: 0 * time.Second},
 			expectedError: "timeout is not valid: duration must not be less than 10s; got 0s",
 		},
-		{
-			name: "output is not valid",
-			args: []string{"bad-output", "1234"},
-			flags: common.CommandListenerCreateFlags{
-				Timeout: 30 * time.Second,
-				Output:  "not-supported",
-			},
-			expectedError: "output type is not valid: value not-supported not allowed. It should be one of this options: [json yaml]",
-		},
 		{
 			name: "flags all valid",
 			args: []string{"my-listener-flags", "8080"},
@@ -158,7 +149,6 @@ func TestCmdListenerCreate_ValidateInput(t *testing.T) {
 				TlsCredentials: "secretname",
 				ListenerType:   "tcp",
 				Timeout:        1 * time.Minute,
-				Output:         "json",
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.Listener{
@@ -219,7 +209,6 @@ func TestCmdListenerCreate_InputToOptions(t *testing.T) {
 		expectedHost           string
 		expectedRoutingKey     string
 		expectedListenerType   string
-		expectedOutput         string
 		expectedTimeout        time.Duration
 		expectedStatus         string
 	}
@@ -227,25 +216,23 @@ func TestCmdListenerCreate_InputToOptions(t *testing.T) {
 	testTable := []test{
 		{
 			name:                   "test1",
-			flags:                  common.CommandListenerCreateFlags{"backend", "backend", "secret", "tcp", 20 * time.Second, "json", "configured"},
+			flags:                  common.CommandListenerCreateFlags{"backend", "backend", "secret", "tcp", 20 * time.Second, "configured"},
 			expectedTlsCredentials: "secret",
 			expectedHost:           "backend",
 			expectedRoutingKey:     "backend",
 			expectedTimeout:        20 * time.Second,
 			expectedListenerType:   "tcp",
-			expectedOutput:         "json",
 			expectedStatus:         "configured",
 		},
 		{
 			name:                   "test2",
-			flags:                  common.CommandListenerCreateFlags{"", "", "secret", "tcp", 30 * time.Second, "yaml", "configured"},
+			flags:                  common.CommandListenerCreateFlags{"", "", "secret", "tcp", 30 * time.Second, "ready"},
 			expectedTlsCredentials: "secret",
 			expectedHost:           "test2",
 			expectedRoutingKey:     "test2",
 			expectedTimeout:        30 * time.Second,
 			expectedListenerType:   "tcp",
-			expectedOutput:         "yaml",
-			expectedStatus:         "configured",
+			expectedStatus:         "ready",
 		},
 	}
 
@@ -261,11 +248,9 @@ func TestCmdListenerCreate_InputToOptions(t *testing.T) {
 			cmd.InputToOptions()
 
 			assert.Check(t, cmd.routingKey == test.expectedRoutingKey)
-			assert.Check(t, cmd.output == test.expectedOutput)
 			assert.Check(t, cmd.tlsCredentials == test.expectedTlsCredentials)
 			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)
 			assert.Check(t, cmd.status == test.expectedStatus)
 		})
@@ -296,18 +281,6 @@ func TestCmdListenerCreate_Run(t *testing.T) {
 				TlsCredentials: "secretname",
 			},
 		},
-		{
-			name:         "output yaml",
-			listenerName: "run-listener",
-			listenerPort: 8080,
-			flags: common.CommandListenerCreateFlags{
-				ListenerType:   "tcp",
-				Host:           "hostname",
-				RoutingKey:     "keyname",
-				TlsCredentials: "secretname",
-				Output:         "yaml",
-			},
-		},
 	}
 
 	for _, test := range testTable {
@@ -316,7 +289,6 @@ func TestCmdListenerCreate_Run(t *testing.T) {
 		cmd.name = test.listenerName
 		cmd.port = test.listenerPort
 		cmd.Flags = &test.flags
-		cmd.output = cmd.Flags.Output
 		cmd.namespace = "test"
 
 		t.Run(test.name, func(t *testing.T) {
@@ -333,7 +305,6 @@ func TestCmdListenerCreate_Run(t *testing.T) {
 func TestCmdListenerCreate_WaitUntil(t *testing.T) {
 	type test struct {
 		name                string
-		output              string
 		status              string
 		k8sObjects          []runtime.Object
 		skupperObjects      []runtime.Object
@@ -385,29 +356,6 @@ func TestCmdListenerCreate_WaitUntil(t *testing.T) {
 			},
 			expectError: false,
 		},
-		{
-			name:   "listener is configured yaml output",
-			output: "yaml",
-			skupperObjects: []runtime.Object{
-				&v2alpha1.Listener{
-					ObjectMeta: v1.ObjectMeta{
-						Name:      "my-listener",
-						Namespace: "test",
-					},
-					Status: v2alpha1.ListenerStatus{
-						Status: v2alpha1.Status{
-							Conditions: []v1.Condition{
-								{
-									Type:   "Configured",
-									Status: "True",
-								},
-							},
-						},
-					},
-				},
-			},
-			expectError: false,
-		},
 		{
 			name:       "user does not wait",
 			status:     "none",
@@ -467,7 +415,6 @@ func TestCmdListenerCreate_WaitUntil(t *testing.T) {
 
 		cmd.name = "my-listener"
 		cmd.port = 8080
-		cmd.output = test.output
 		cmd.timeout = 1 * time.Second
 		cmd.namespace = "test"
 		cmd.status = test.status
diff --git a/internal/cmd/skupper/listener/kube/listener_generate_test.go b/internal/cmd/skupper/listener/kube/listener_generate_test.go
new file mode 100644
index 00000000..a0383701
--- /dev/null
+++ b/internal/cmd/skupper/listener/kube/listener_generate_test.go
@@ -0,0 +1,281 @@
+package kube
+
+import (
+	"testing"
+
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/testutils"
+
+	"github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
+	"gotest.tools/v3/assert"
+	v12 "k8s.io/api/core/v1"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+)
+
+func TestCmdListenerGenerate_ValidateInput(t *testing.T) {
+	type test struct {
+		name                string
+		args                []string
+		flags               common.CommandListenerGenerateFlags
+		k8sObjects          []runtime.Object
+		skupperObjects      []runtime.Object
+		skupperErrorMessage string
+		expectedError       string
+	}
+
+	testTable := []test{
+		{
+			name:          "listener name and port are not specified",
+			args:          []string{},
+			flags:         common.CommandListenerGenerateFlags{},
+			expectedError: "listener name and port must be configured",
+		},
+		{
+			name:          "listener name empty",
+			args:          []string{"", "8090"},
+			flags:         common.CommandListenerGenerateFlags{},
+			expectedError: "listener name must not be empty",
+		},
+		{
+			name:          "listener port empty",
+			args:          []string{"my-name-port-empty", ""},
+			flags:         common.CommandListenerGenerateFlags{},
+			expectedError: "listener port must not be empty",
+		},
+		{
+			name:          "listener port not positive",
+			args:          []string{"my-port-positive", "-45"},
+			flags:         common.CommandListenerGenerateFlags{},
+			expectedError: "listener port is not valid: value is not positive",
+		},
+		{
+			name:          "listener name and port are not specified",
+			args:          []string{},
+			flags:         common.CommandListenerGenerateFlags{},
+			expectedError: "listener name and port must be configured",
+		},
+		{
+			name:          "listener port is not specified",
+			args:          []string{"my-name"},
+			flags:         common.CommandListenerGenerateFlags{},
+			expectedError: "listener name and port must be configured",
+		},
+		{
+			name:          "more than two arguments are specified",
+			args:          []string{"my", "listener", "8080"},
+			flags:         common.CommandListenerGenerateFlags{},
+			expectedError: "only two arguments are allowed for this command",
+		},
+		{
+			name:          "listener name is not valid.",
+			args:          []string{"my new listener", "8080"},
+			flags:         common.CommandListenerGenerateFlags{},
+			expectedError: "listener name is not valid: value does not match this regular expression: ^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$",
+		},
+		{
+			name:          "port is not valid.",
+			args:          []string{"my-listener-port", "abcd"},
+			flags:         common.CommandListenerGenerateFlags{},
+			expectedError: "listener port is not valid: strconv.Atoi: parsing \"abcd\": invalid syntax",
+		},
+		{
+			name:          "listener type is not valid",
+			args:          []string{"my-listener-type", "8080"},
+			flags:         common.CommandListenerGenerateFlags{ListenerType: "not-valid"},
+			expectedError: "listener type is not valid: value not-valid not allowed. It should be one of this options: [tcp]",
+		},
+		{
+			name:          "routing key is not valid",
+			args:          []string{"my-listener-rk", "8080"},
+			flags:         common.CommandListenerGenerateFlags{RoutingKey: "not-valid$"},
+			expectedError: "routing key is not valid: value does not match this regular expression: ^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$",
+		},
+		{
+			name:          "tls-credentials does not exist",
+			args:          []string{"my-listener-tls", "8080"},
+			flags:         common.CommandListenerGenerateFlags{TlsCredentials: "not-&valid"},
+			expectedError: "tlsCredentials is not valid: value does not match this regular expression: ^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$",
+		},
+		{
+			name:          "output is not valid",
+			args:          []string{"bad-output", "1234"},
+			flags:         common.CommandListenerGenerateFlags{Output: "not-supported"},
+			expectedError: "output type is not valid: value not-supported not allowed. It should be one of this options: [json yaml]",
+		},
+		{
+			name: "flags all valid",
+			args: []string{"my-listener-flags", "8080"},
+			flags: common.CommandListenerGenerateFlags{
+				Host:           "hostname",
+				RoutingKey:     "routingkeyname",
+				TlsCredentials: "secretname",
+				ListenerType:   "tcp",
+				Output:         "json",
+			},
+			skupperObjects: []runtime.Object{
+				&v2alpha1.Listener{
+					ObjectMeta: v1.ObjectMeta{
+						Name:      "my-listener",
+						Namespace: "test",
+					},
+					Status: v2alpha1.ListenerStatus{
+						Status: v2alpha1.Status{
+							Conditions: []v1.Condition{
+								{
+									Type:   "Configured",
+									Status: "True",
+								},
+							},
+						},
+					},
+				},
+			},
+			k8sObjects: []runtime.Object{
+				&v12.Secret{
+					ObjectMeta: v1.ObjectMeta{
+						Name:      "secretname",
+						Namespace: "test",
+					},
+				},
+			},
+		},
+	}
+
+	for _, test := range testTable {
+		t.Run(test.name, func(t *testing.T) {
+
+			command, err := newCmdListenerGenerateWithMocks("test", test.k8sObjects, test.skupperObjects, "")
+			assert.Assert(t, err)
+
+			command.Flags = &test.flags
+
+			testutils.CheckValidateInput(t, command, test.expectedError, test.args)
+
+		})
+	}
+}
+
+func TestCmdListenerGenerate_InputToOptions(t *testing.T) {
+
+	type test struct {
+		name                   string
+		flags                  common.CommandListenerGenerateFlags
+		Listenername           string
+		expectedTlsCredentials string
+		expectedHost           string
+		expectedRoutingKey     string
+		expectedListenerType   string
+		expectedOutput         string
+	}
+
+	testTable := []test{
+		{
+			name:                   "test1",
+			flags:                  common.CommandListenerGenerateFlags{"backend", "backend", "secret", "tcp", "json"},
+			expectedTlsCredentials: "secret",
+			expectedHost:           "backend",
+			expectedRoutingKey:     "backend",
+			expectedListenerType:   "tcp",
+			expectedOutput:         "json",
+		},
+		{
+			name:                   "test2",
+			flags:                  common.CommandListenerGenerateFlags{"", "", "secret", "tcp", "yaml"},
+			expectedTlsCredentials: "secret",
+			expectedHost:           "test2",
+			expectedRoutingKey:     "test2",
+			expectedListenerType:   "tcp",
+			expectedOutput:         "yaml",
+		},
+	}
+
+	for _, test := range testTable {
+		t.Run(test.name, func(t *testing.T) {
+
+			cmd, err := newCmdListenerGenerateWithMocks("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.tlsCredentials == test.expectedTlsCredentials)
+			assert.Check(t, cmd.host == test.expectedHost)
+			assert.Check(t, cmd.output == test.expectedOutput)
+			assert.Check(t, cmd.listenerType == test.expectedListenerType)
+		})
+	}
+}
+
+func TestCmdListenerGenerate_Run(t *testing.T) {
+	type test struct {
+		name                string
+		listenerName        string
+		listenerPort        int
+		flags               common.CommandListenerGenerateFlags
+		k8sObjects          []runtime.Object
+		skupperObjects      []runtime.Object
+		skupperErrorMessage string
+		errorMessage        string
+	}
+
+	testTable := []test{
+		{
+			name:         "runs ok",
+			listenerName: "run-listener",
+			listenerPort: 8080,
+			flags: common.CommandListenerGenerateFlags{
+				ListenerType:   "tcp",
+				Host:           "hostname",
+				RoutingKey:     "keyname",
+				TlsCredentials: "secretname",
+				Output:         "json",
+			},
+		},
+		{
+			name:         "output yaml",
+			listenerName: "run-listener",
+			listenerPort: 8080,
+			flags: common.CommandListenerGenerateFlags{
+				ListenerType:   "tcp",
+				Host:           "hostname",
+				RoutingKey:     "keyname",
+				TlsCredentials: "secretname",
+				Output:         "yaml",
+			},
+		},
+	}
+
+	for _, test := range testTable {
+		cmd, err := newCmdListenerGenerateWithMocks("test", test.k8sObjects, test.skupperObjects, test.skupperErrorMessage)
+		assert.Assert(t, err)
+		cmd.name = test.listenerName
+		cmd.port = test.listenerPort
+		cmd.Flags = &test.flags
+		cmd.output = cmd.Flags.Output
+		cmd.namespace = "test"
+
+		t.Run(test.name, func(t *testing.T) {
+			err := cmd.Run()
+			if err != nil {
+				assert.Check(t, test.errorMessage == err.Error())
+			} else {
+				assert.Check(t, err == nil)
+			}
+		})
+	}
+}
+
+// --- helper methods
+
+func newCmdListenerGenerateWithMocks(namespace string, k8sObjects []runtime.Object, skupperObjects []runtime.Object, fakeSkupperError string) (*CmdListenerGenerate, error) {
+
+	cmdListenerGenerate := &CmdListenerGenerate{
+		namespace: namespace,
+	}
+	return cmdListenerGenerate, nil
+}
diff --git a/internal/cmd/skupper/listener/kube/listener_update_test.go b/internal/cmd/skupper/listener/kube/listener_update_test.go
index 40859221..5ac162c4 100644
--- a/internal/cmd/skupper/listener/kube/listener_update_test.go
+++ b/internal/cmd/skupper/listener/kube/listener_update_test.go
@@ -188,33 +188,6 @@ func TestCmdListenerUpdate_ValidateInput(t *testing.T) {
 			},
 			expectedError: "timeout is not valid: duration must not be less than 10s; got 5s",
 		},
-		{
-			name: "output is not valid",
-			args: []string{"bad-output"},
-			flags: common.CommandListenerUpdateFlags{
-				Output:  "not-supported",
-				Timeout: 10 * time.Second,
-			},
-			skupperObjects: []runtime.Object{
-				&v2alpha1.Listener{
-					ObjectMeta: v1.ObjectMeta{
-						Name:      "bad-output",
-						Namespace: "test",
-					},
-					Status: v2alpha1.ListenerStatus{
-						Status: v2alpha1.Status{
-							Conditions: []v1.Condition{
-								{
-									Type:   "Configured",
-									Status: "True",
-								},
-							},
-						},
-					},
-				},
-			},
-			expectedError: "output type is not valid: value not-supported not allowed. It should be one of this options: [json yaml]",
-		},
 		{
 			name: "flags all valid",
 			args: []string{"my-listener-flags"},
@@ -225,7 +198,6 @@ func TestCmdListenerUpdate_ValidateInput(t *testing.T) {
 				Port:           1234,
 				ListenerType:   "tcp",
 				Timeout:        10 * time.Second,
-				Output:         "json",
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.Listener{
@@ -346,7 +318,6 @@ func TestCmdListenerUpdate_Run(t *testing.T) {
 				Host:           "hostname",
 				RoutingKey:     "keyname",
 				TlsCredentials: "secretname",
-				Output:         "yaml",
 				Timeout:        1 * time.Minute,
 			},
 			skupperObjects: []runtime.Object{
@@ -368,32 +339,6 @@ func TestCmdListenerUpdate_Run(t *testing.T) {
 				},
 			},
 		},
-		{
-			name:         "new output json",
-			listenerName: "run-listener",
-			flags: common.CommandListenerUpdateFlags{
-				Timeout: 1 * time.Minute,
-			},
-			newOutput: "json",
-			skupperObjects: []runtime.Object{
-				&v2alpha1.Listener{
-					ObjectMeta: v1.ObjectMeta{
-						Name:      "run-listener",
-						Namespace: "test",
-					},
-					Status: v2alpha1.ListenerStatus{
-						Status: v2alpha1.Status{
-							Conditions: []v1.Condition{
-								{
-									Type:   "Configured",
-									Status: "True",
-								},
-							},
-						},
-					},
-				},
-			},
-		},
 		{
 			name:                "run fails",
 			listenerName:        "run-listener",
@@ -410,7 +355,6 @@ func TestCmdListenerUpdate_Run(t *testing.T) {
 		cmd.name = test.listenerName
 		cmd.Flags = &test.flags
 		cmd.namespace = "test"
-		cmd.newSettings.output = test.newOutput
 
 		t.Run(test.name, func(t *testing.T) {
 			err := cmd.Run()
@@ -426,7 +370,6 @@ func TestCmdListenerUpdate_Run(t *testing.T) {
 func TestCmdListenerUpdate_WaitUntil(t *testing.T) {
 	type test struct {
 		name                string
-		output              string
 		status              string
 		k8sObjects          []runtime.Object
 		skupperObjects      []runtime.Object
@@ -482,29 +425,6 @@ func TestCmdListenerUpdate_WaitUntil(t *testing.T) {
 			},
 			expectError: false,
 		},
-		{
-			name:   "listener is ready json output",
-			output: "json",
-			skupperObjects: []runtime.Object{
-				&v2alpha1.Listener{
-					ObjectMeta: v1.ObjectMeta{
-						Name:      "my-listener",
-						Namespace: "test",
-					},
-					Status: v2alpha1.ListenerStatus{
-						Status: v2alpha1.Status{
-							Conditions: []v1.Condition{
-								{
-									Type:   "Configured",
-									Status: "True",
-								},
-							},
-						},
-					},
-				},
-			},
-			expectError: false,
-		},
 		{
 			name:       "listener is not ready yet, but user waits for configured",
 			status:     "configured",
@@ -590,10 +510,8 @@ func TestCmdListenerUpdate_WaitUntil(t *testing.T) {
 		cmd.name = "my-listener"
 		cmd.Flags = &common.CommandListenerUpdateFlags{
 			Timeout: 1 * time.Second,
-			Output:  test.output,
 		}
 		cmd.namespace = "test"
-		cmd.newSettings.output = cmd.Flags.Output
 		cmd.status = test.status
 
 		t.Run(test.name, func(t *testing.T) {
diff --git a/internal/cmd/skupper/listener/listener_test.go b/internal/cmd/skupper/listener/listener_test.go
index 79a276c7..abe6c88c 100644
--- a/internal/cmd/skupper/listener/listener_test.go
+++ b/internal/cmd/skupper/listener/listener_test.go
@@ -2,12 +2,13 @@ package listener
 
 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/v3/assert"
-	"testing"
 )
 
 func TestCmdListenerFactory(t *testing.T) {
@@ -26,7 +27,6 @@ func TestCmdListenerFactory(t *testing.T) {
 				common.FlagNameListenerHost:   "",
 				common.FlagNameTlsCredentials: "",
 				common.FlagNameListenerType:   "tcp",
-				common.FlagNameOutput:         "",
 				common.FlagNameTimeout:        "1m0s",
 				common.FlagNameWait:           "configured",
 			},
@@ -39,7 +39,6 @@ func TestCmdListenerFactory(t *testing.T) {
 				common.FlagNameListenerHost:   "",
 				common.FlagNameTlsCredentials: "",
 				common.FlagNameListenerType:   "tcp",
-				common.FlagNameOutput:         "",
 				common.FlagNameTimeout:        "1m0s",
 				common.FlagNameListenerPort:   "0",
 				common.FlagNameWait:           "configured",
@@ -61,6 +60,17 @@ func TestCmdListenerFactory(t *testing.T) {
 			},
 			command: CmdListenerDeleteFactory(types.PlatformKubernetes),
 		},
+		{
+			name: "CmdListenerGenerateFactory",
+			expectedFlagsWithDefaultValue: map[string]interface{}{
+				common.FlagNameRoutingKey:     "",
+				common.FlagNameListenerHost:   "",
+				common.FlagNameTlsCredentials: "",
+				common.FlagNameListenerType:   "tcp",
+				common.FlagNameOutput:         "yaml",
+			},
+			command: CmdListenerGenerateFactory(types.PlatformKubernetes),
+		},
 	}
 
 	for _, test := range testTable {
diff --git a/internal/cmd/skupper/listener/nonkube/listener_create_test.go b/internal/cmd/skupper/listener/nonkube/listener_create_test.go
index 65d2e5c1..b4eed6bf 100644
--- a/internal/cmd/skupper/listener/nonkube/listener_create_test.go
+++ b/internal/cmd/skupper/listener/nonkube/listener_create_test.go
@@ -91,16 +91,9 @@ func TestNonKubeCmdListenerCreate_ValidateInput(t *testing.T) {
 			expectedError: "host is not valid: a valid IP address or hostname is expected",
 		},
 		{
-			name:          "output format is not valid",
-			args:          []string{"my-listener", "8080"},
-			flags:         &common.CommandListenerCreateFlags{Output: "not-valid", Host: "1.2.3.4"},
-			expectedError: "output type is not valid: value not-valid not allowed. It should be one of this options: [json yaml]",
-		},
-		{
-			name:          "kubernetes flags are not valid on this platform",
-			args:          []string{"my-listener", "8080"},
-			flags:         &common.CommandListenerCreateFlags{Host: "1.2.3.4"},
-			expectedError: "",
+			name:  "kubernetes flags are not valid on this platform",
+			args:  []string{"my-listener", "8080"},
+			flags: &common.CommandListenerCreateFlags{Host: "1.2.3.4"},
 			cobraGenericFlags: map[string]string{
 				common.FlagNameContext:    "test",
 				common.FlagNameKubeconfig: "test",
@@ -113,10 +106,8 @@ func TestNonKubeCmdListenerCreate_ValidateInput(t *testing.T) {
 				RoutingKey:     "routingkeyname",
 				TlsCredentials: "secretname",
 				ListenerType:   "tcp",
-				Output:         "json",
 				Host:           "1.2.3.4",
 			},
-			expectedError: "",
 		},
 	}
 
@@ -153,40 +144,36 @@ func TestNonKubeCmdListenerCreate_InputToOptions(t *testing.T) {
 		expectedHost           string
 		expectedRoutingKey     string
 		expectedListenerType   string
-		expectedOutput         string
 	}
 
 	testTable := []test{
 		{
 			name:                   "test1",
-			flags:                  common.CommandListenerCreateFlags{"backend", "", "secret", "tcp", 0, "json", "none"},
+			flags:                  common.CommandListenerCreateFlags{"backend", "", "secret", "tcp", 0, "none"},
 			expectedTlsCredentials: "secret",
 			expectedHost:           "0.0.0.0",
 			expectedRoutingKey:     "backend",
 			expectedListenerType:   "tcp",
-			expectedOutput:         "json",
 			expectedNamespace:      "default",
 		},
 		{
 			name:                   "test2",
 			namespace:              "test",
-			flags:                  common.CommandListenerCreateFlags{"backend", "1.2.3.4", "secret", "tcp", 0, "json", "configured"},
+			flags:                  common.CommandListenerCreateFlags{"backend", "1.2.3.4", "secret", "tcp", 0, "configured"},
 			expectedTlsCredentials: "secret",
 			expectedHost:           "1.2.3.4",
 			expectedRoutingKey:     "backend",
 			expectedListenerType:   "tcp",
-			expectedOutput:         "json",
 			expectedNamespace:      "test",
 		},
 		{
 			name:                   "test3",
 			namespace:              "default",
-			flags:                  common.CommandListenerCreateFlags{"", "", "secret", "tcp", 0, "yaml", "ready"},
+			flags:                  common.CommandListenerCreateFlags{"", "", "secret", "tcp", 0, "ready"},
 			expectedTlsCredentials: "secret",
 			expectedHost:           "0.0.0.0",
 			expectedRoutingKey:     "my-listener",
 			expectedListenerType:   "tcp",
-			expectedOutput:         "yaml",
 			expectedNamespace:      "default",
 		},
 	}
@@ -205,7 +192,6 @@ func TestNonKubeCmdListenerCreate_InputToOptions(t *testing.T) {
 			assert.Check(t, cmd.tlsCredentials == test.expectedTlsCredentials)
 			assert.Check(t, cmd.host == test.expectedHost)
 			assert.Check(t, cmd.listenerType == test.expectedListenerType)
-			assert.Check(t, cmd.output == test.expectedOutput)
 			assert.Check(t, cmd.namespace == test.expectedNamespace)
 		})
 	}
@@ -219,7 +205,6 @@ func TestNonKubeCmdListenerCreate_Run(t *testing.T) {
 		skupperError   string
 		listenerName   string
 		host           string
-		output         string
 		errorMessage   string
 		routingKey     string
 		tlsCredentials string
@@ -229,7 +214,7 @@ func TestNonKubeCmdListenerCreate_Run(t *testing.T) {
 
 	testTable := []test{
 		{
-			name:           "runs ok",
+			name:           "test1",
 			k8sObjects:     nil,
 			skupperObjects: nil,
 			listenerName:   "test1",
@@ -240,24 +225,20 @@ func TestNonKubeCmdListenerCreate_Run(t *testing.T) {
 			host:           "1.2.3.4",
 		},
 		{
-			name:           "runs ok yaml",
+			name:           "test2",
 			k8sObjects:     nil,
 			skupperObjects: nil,
 			listenerName:   "test2",
 			listenerPort:   8080,
 			listenerType:   "tcp",
 			host:           "2.2.2.2",
-			output:         "yaml",
 		},
 		{
-			name:           "runs fails because the output format is not supported",
+			name:           "test3",
 			k8sObjects:     nil,
 			skupperObjects: nil,
 			listenerName:   "test3",
 			listenerPort:   8080,
-			host:           "3.3.3.3",
-			output:         "unsupported",
-			errorMessage:   "format unsupported not supported",
 		},
 	}
 
@@ -265,7 +246,6 @@ func TestNonKubeCmdListenerCreate_Run(t *testing.T) {
 		command := &CmdListenerCreate{}
 
 		command.listenerName = test.listenerName
-		command.output = test.output
 		command.port = test.listenerPort
 		command.host = test.host
 		command.listenerType = test.listenerType
diff --git a/internal/cmd/skupper/listener/nonkube/listener_generate_test.go b/internal/cmd/skupper/listener/nonkube/listener_generate_test.go
new file mode 100644
index 00000000..0db146b8
--- /dev/null
+++ b/internal/cmd/skupper/listener/nonkube/listener_generate_test.go
@@ -0,0 +1,285 @@
+package nonkube
+
+import (
+	"testing"
+
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/testutils"
+	"github.com/skupperproject/skupper/internal/nonkube/client/fs"
+	"github.com/spf13/cobra"
+
+	"gotest.tools/v3/assert"
+	"k8s.io/apimachinery/pkg/runtime"
+)
+
+func TestNonKubeCmdListenerGenerate_ValidateInput(t *testing.T) {
+	type test struct {
+		name              string
+		args              []string
+		k8sObjects        []runtime.Object
+		skupperObjects    []runtime.Object
+		flags             *common.CommandListenerGenerateFlags
+		cobraGenericFlags map[string]string
+		expectedError     string
+	}
+
+	testTable := []test{
+		{
+			name:          "listener name and port are not specified",
+			args:          []string{},
+			flags:         &common.CommandListenerGenerateFlags{Host: "1.2.3.4"},
+			expectedError: "listener name and port must be configured",
+		},
+		{
+			name:          "listener name is not valid",
+			args:          []string{"my new Listener", "8080"},
+			flags:         &common.CommandListenerGenerateFlags{Host: "1.2.3.4"},
+			expectedError: "listener name is not valid: value does not match this regular expression: ^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$",
+		},
+		{
+			name:          "listener name is empty",
+			args:          []string{"", "1234"},
+			flags:         &common.CommandListenerGenerateFlags{Host: "1.2.3.4"},
+			expectedError: "listener name must not be empty",
+		},
+		{
+			name:          "listener port empty",
+			args:          []string{"my-name-port-empty", ""},
+			flags:         &common.CommandListenerGenerateFlags{Host: "1.2.3.4"},
+			expectedError: "listener port must not be empty",
+		},
+		{
+			name:          "port is not valid",
+			args:          []string{"my-listener-port", "abcd"},
+			flags:         &common.CommandListenerGenerateFlags{Host: "1.2.3.4"},
+			expectedError: "listener port is not valid: strconv.Atoi: parsing \"abcd\": invalid syntax",
+		},
+		{
+			name:          "listener port not positive",
+			args:          []string{"my-port-positive", "-45"},
+			flags:         &common.CommandListenerGenerateFlags{Host: "1.2.3.4"},
+			expectedError: "listener port is not valid: value is not positive",
+		},
+		{
+			name:          "more than two arguments was specified",
+			args:          []string{"my", "listener", "test"},
+			flags:         &common.CommandListenerGenerateFlags{Host: "1.2.3.4"},
+			expectedError: "only two arguments are allowed for this command",
+		},
+		{
+			name:          "type is not valid",
+			args:          []string{"my-listener", "8080"},
+			flags:         &common.CommandListenerGenerateFlags{ListenerType: "not-valid", Host: "1.2.3.4"},
+			expectedError: "listener type is not valid: value not-valid not allowed. It should be one of this options: [tcp]",
+		},
+		{
+			name:          "routing key is not valid",
+			args:          []string{"my-listener-rk", "8080"},
+			flags:         &common.CommandListenerGenerateFlags{RoutingKey: "not-valid$", Host: "1.2.3.4"},
+			expectedError: "routing key is not valid: value does not match this regular expression: ^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$",
+		},
+		{
+			name:          "TlsCredentials key is not valid",
+			args:          []string{"my-listener-tls", "8080"},
+			flags:         &common.CommandListenerGenerateFlags{TlsCredentials: "not-valid$", Host: "1.2.3.4"},
+			expectedError: "tlsCredentials is not valid: value does not match this regular expression: ^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$",
+		},
+		{
+			name:          "host is not valid",
+			args:          []string{"my-listener-host", "8080"},
+			flags:         &common.CommandListenerGenerateFlags{Host: "not-valid$"},
+			expectedError: "host is not valid: a valid IP address or hostname is expected",
+		},
+		{
+			name:          "output format is not valid",
+			args:          []string{"my-listener", "8080"},
+			flags:         &common.CommandListenerGenerateFlags{Output: "not-valid", Host: "1.2.3.4"},
+			expectedError: "output type is not valid: value not-valid not allowed. It should be one of this options: [json yaml]",
+		},
+		{
+			name:  "kubernetes flags are not valid on this platform",
+			args:  []string{"my-listener", "8080"},
+			flags: &common.CommandListenerGenerateFlags{Host: "1.2.3.4"},
+			cobraGenericFlags: map[string]string{
+				common.FlagNameContext:    "test",
+				common.FlagNameKubeconfig: "test",
+			},
+		},
+		{
+			name: "flags all valid",
+			args: []string{"my-listener-flags", "8080"},
+			flags: &common.CommandListenerGenerateFlags{
+				RoutingKey:     "routingkeyname",
+				TlsCredentials: "secretname",
+				ListenerType:   "tcp",
+				Output:         "json",
+				Host:           "1.2.3.4",
+			},
+		},
+	}
+
+	for _, test := range testTable {
+		t.Run(test.name, func(t *testing.T) {
+			command := &CmdListenerGenerate{Flags: &common.CommandListenerGenerateFlags{}}
+			command.CobraCmd = &cobra.Command{Use: "test"}
+
+			if test.flags != nil {
+				command.Flags = test.flags
+			}
+
+			if test.cobraGenericFlags != nil && len(test.cobraGenericFlags) > 0 {
+				for name, value := range test.cobraGenericFlags {
+					command.CobraCmd.Flags().String(name, value, "")
+				}
+			}
+
+			testutils.CheckValidateInput(t, command, test.expectedError, test.args)
+
+		})
+	}
+}
+
+func TestNonKubeCmdListenerGenerate_InputToOptions(t *testing.T) {
+
+	type test struct {
+		name                   string
+		args                   []string
+		namespace              string
+		flags                  common.CommandListenerGenerateFlags
+		expectedNamespace      string
+		listenerName           string
+		expectedTlsCredentials string
+		expectedHost           string
+		expectedRoutingKey     string
+		expectedListenerType   string
+		expectedOutput         string
+	}
+
+	testTable := []test{
+		{
+			name:                   "test1",
+			flags:                  common.CommandListenerGenerateFlags{"backend", "", "secret", "tcp", "json"},
+			expectedTlsCredentials: "secret",
+			expectedHost:           "0.0.0.0",
+			expectedRoutingKey:     "backend",
+			expectedListenerType:   "tcp",
+			expectedOutput:         "json",
+			expectedNamespace:      "default",
+		},
+		{
+			name:                   "test2",
+			namespace:              "test",
+			flags:                  common.CommandListenerGenerateFlags{"backend", "1.2.3.4", "secret", "tcp", "json"},
+			expectedTlsCredentials: "secret",
+			expectedHost:           "1.2.3.4",
+			expectedRoutingKey:     "backend",
+			expectedListenerType:   "tcp",
+			expectedOutput:         "json",
+			expectedNamespace:      "test",
+		},
+		{
+			name:                   "test3",
+			namespace:              "default",
+			flags:                  common.CommandListenerGenerateFlags{"", "", "secret", "tcp", "yaml"},
+			expectedTlsCredentials: "secret",
+			expectedHost:           "0.0.0.0",
+			expectedRoutingKey:     "my-listener",
+			expectedListenerType:   "tcp",
+			expectedOutput:         "yaml",
+			expectedNamespace:      "default",
+		},
+	}
+
+	for _, test := range testTable {
+		t.Run(test.name, func(t *testing.T) {
+			cmd := CmdListenerGenerate{}
+			cmd.Flags = &test.flags
+			cmd.listenerName = "my-listener"
+			cmd.namespace = test.namespace
+			cmd.listenerHandler = fs.NewListenerHandler(cmd.namespace)
+
+			cmd.InputToOptions()
+
+			assert.Check(t, cmd.routingKey == test.expectedRoutingKey)
+			assert.Check(t, cmd.tlsCredentials == test.expectedTlsCredentials)
+			assert.Check(t, cmd.host == test.expectedHost)
+			assert.Check(t, cmd.listenerType == test.expectedListenerType)
+			assert.Check(t, cmd.output == test.expectedOutput)
+			assert.Check(t, cmd.namespace == test.expectedNamespace)
+		})
+	}
+}
+
+func TestNonKubeCmdListenerGenerate_Run(t *testing.T) {
+	type test struct {
+		name           string
+		k8sObjects     []runtime.Object
+		skupperObjects []runtime.Object
+		skupperError   string
+		listenerName   string
+		host           string
+		output         string
+		errorMessage   string
+		routingKey     string
+		tlsCredentials string
+		listenerType   string
+		listenerPort   int
+	}
+
+	testTable := []test{
+		{
+			name:           "runs ok",
+			k8sObjects:     nil,
+			skupperObjects: nil,
+			listenerName:   "test1",
+			listenerPort:   8080,
+			listenerType:   "tcp",
+			routingKey:     "keyname",
+			tlsCredentials: "secretname",
+			host:           "1.2.3.4",
+			output:         "json",
+		},
+		{
+			name:           "runs ok yaml",
+			k8sObjects:     nil,
+			skupperObjects: nil,
+			listenerName:   "test2",
+			listenerPort:   8080,
+			listenerType:   "tcp",
+			host:           "2.2.2.2",
+			output:         "yaml",
+		},
+		{
+			name:           "runs fails because the output format is not supported",
+			k8sObjects:     nil,
+			skupperObjects: nil,
+			listenerName:   "test3",
+			listenerPort:   8080,
+			host:           "3.3.3.3",
+			output:         "unsupported",
+			errorMessage:   "format unsupported not supported",
+		},
+	}
+
+	for _, test := range testTable {
+		command := &CmdListenerGenerate{}
+
+		command.listenerName = test.listenerName
+		command.output = test.output
+		command.port = test.listenerPort
+		command.host = test.host
+		command.listenerType = test.listenerType
+		command.namespace = "test"
+		command.listenerHandler = fs.NewListenerHandler(command.namespace)
+		defer command.listenerHandler.Delete("test1")
+		t.Run(test.name, func(t *testing.T) {
+
+			err := command.Run()
+			if err != nil {
+				assert.Check(t, test.errorMessage == err.Error(), err.Error())
+			} else {
+				assert.Check(t, err == nil)
+			}
+		})
+	}
+}
diff --git a/internal/cmd/skupper/listener/nonkube/listener_update_test.go b/internal/cmd/skupper/listener/nonkube/listener_update_test.go
index eec08956..145dd758 100644
--- a/internal/cmd/skupper/listener/nonkube/listener_update_test.go
+++ b/internal/cmd/skupper/listener/nonkube/listener_update_test.go
@@ -92,12 +92,6 @@ func TestCmdListenerUpdate_ValidateInput(t *testing.T) {
 			flags:         &common.CommandListenerUpdateFlags{Host: "not-valid$"},
 			expectedError: "host is not valid: a valid IP address or hostname is expected",
 		},
-		{
-			name:          "output is not valid",
-			args:          []string{"my-listener"},
-			flags:         &common.CommandListenerUpdateFlags{Output: "not-supported"},
-			expectedError: "output type is not valid: value not-supported not allowed. It should be one of this options: [json yaml]",
-		},
 		{
 			name:  "kubernetes flags are not valid on this platform",
 			args:  []string{"my-listener"},
@@ -116,7 +110,6 @@ func TestCmdListenerUpdate_ValidateInput(t *testing.T) {
 				TlsCredentials: "secretname",
 				Port:           1234,
 				ListenerType:   "tcp",
-				Output:         "json",
 				Host:           "1.2.3.4",
 			},
 			expectedError: "",
@@ -175,7 +168,6 @@ func TestCmdListenerUpdate_Run(t *testing.T) {
 		errorMessage        string
 		listenerName        string
 		host                string
-		output              string
 		routingKey          string
 		tlsCredentials      string
 		listenerType        string
@@ -194,14 +186,12 @@ func TestCmdListenerUpdate_Run(t *testing.T) {
 			tlsCredentials: "secretname",
 		},
 		{
-			name:           "run output json",
-			listenerName:   "my-listener",
-			port:           8181,
-			listenerType:   "tcp",
-			host:           "hostname",
-			routingKey:     "keyname",
-			tlsCredentials: "secretname",
-			output:         "json",
+			name:         "run ok no secret",
+			listenerName: "my-listener",
+			port:         8181,
+			listenerType: "tcp",
+			host:         "hostname",
+			routingKey:   "keyname",
 		},
 	}
 
@@ -209,7 +199,6 @@ func TestCmdListenerUpdate_Run(t *testing.T) {
 		command := &CmdListenerUpdate{}
 		command.CobraCmd = &cobra.Command{Use: "test"}
 		command.listenerName = test.listenerName
-		command.newSettings.output = test.output
 		command.newSettings.port = test.port
 		command.newSettings.host = test.host
 		command.newSettings.routingKey = test.routingKey
diff --git a/internal/cmd/skupper/site/kube/site_create_test.go b/internal/cmd/skupper/site/kube/site_create_test.go
index d9766654..18a04b1e 100644
--- a/internal/cmd/skupper/site/kube/site_create_test.go
+++ b/internal/cmd/skupper/site/kube/site_create_test.go
@@ -66,12 +66,6 @@ func TestCmdSiteCreate_ValidateInput(t *testing.T) {
 			flags:         &common.CommandSiteCreateFlags{ServiceAccount: "not valid service account name", Timeout: time.Minute},
 			expectedError: "service account name is not valid: serviceaccounts \"not valid service account name\" not found",
 		},
-		{
-			name:          "host name was specified, but this flag does not work on kube platforms",
-			args:          []string{"my-site"},
-			flags:         &common.CommandSiteCreateFlags{Host: "host", Timeout: time.Minute},
-			expectedError: "",
-		},
 		{
 			name:  "link access type is not valid",
 			args:  []string{"my-site"},
@@ -80,16 +74,9 @@ func TestCmdSiteCreate_ValidateInput(t *testing.T) {
 				"for the site to work with this type of linkAccess, the --enable-link-access option must be set to true",
 		},
 		{
-			name:          "output format is not valid",
-			args:          []string{"my-site"},
-			flags:         &common.CommandSiteCreateFlags{Output: "not-valid", Timeout: time.Minute},
-			expectedError: "output type is not valid: value not-valid not allowed. It should be one of this options: [json yaml]",
-		},
-		{
-			name:          "host flag is not valid for this platform",
-			args:          []string{"my-site"},
-			flags:         &common.CommandSiteCreateFlags{Host: "host", Timeout: time.Minute},
-			expectedError: "",
+			name:  "bind-host flag is not valid for this platform",
+			args:  []string{"my-site"},
+			flags: &common.CommandSiteCreateFlags{BindHost: "host", Timeout: time.Minute},
 		},
 		{
 			name:          "subject alternative names flag is not valid for this platform",
@@ -142,7 +129,6 @@ func TestCmdSiteCreate_InputToOptions(t *testing.T) {
 		args               []string
 		flags              common.CommandSiteCreateFlags
 		expectedLinkAccess string
-		expectedOutput     string
 		expectedTimeout    time.Duration
 		expectedStatus     string
 	}
@@ -153,42 +139,30 @@ func TestCmdSiteCreate_InputToOptions(t *testing.T) {
 			args:               []string{"my-site"},
 			flags:              common.CommandSiteCreateFlags{},
 			expectedLinkAccess: "",
-			expectedOutput:     "",
 		},
 		{
 			name:               "options with link access enabled but using a type by default",
 			args:               []string{"my-site"},
 			flags:              common.CommandSiteCreateFlags{EnableLinkAccess: true},
 			expectedLinkAccess: "default",
-			expectedOutput:     "",
 		},
 		{
 			name:               "options with link access enabled using the nodeport type",
 			args:               []string{"my-site"},
 			flags:              common.CommandSiteCreateFlags{EnableLinkAccess: true, LinkAccessType: "nodeport"},
 			expectedLinkAccess: "nodeport",
-			expectedOutput:     "",
 		},
 		{
 			name:               "options with link access options not well specified",
 			args:               []string{"my-site"},
 			flags:              common.CommandSiteCreateFlags{EnableLinkAccess: false, LinkAccessType: "nodeport"},
 			expectedLinkAccess: "",
-			expectedOutput:     "",
-		},
-		{
-			name:               "options output type",
-			args:               []string{"my-site"},
-			flags:              common.CommandSiteCreateFlags{EnableLinkAccess: false, LinkAccessType: "nodeport", Output: "yaml"},
-			expectedLinkAccess: "",
-			expectedOutput:     "yaml",
 		},
 		{
 			name:               "options with timeout",
 			args:               []string{"my-site"},
 			flags:              common.CommandSiteCreateFlags{Timeout: time.Second * 60},
 			expectedLinkAccess: "",
-			expectedOutput:     "",
 			expectedTimeout:    time.Minute,
 		},
 		{
@@ -208,7 +182,6 @@ func TestCmdSiteCreate_InputToOptions(t *testing.T) {
 
 			cmd.InputToOptions()
 
-			assert.Check(t, cmd.output == test.expectedOutput)
 			assert.Check(t, cmd.linkAccessType == test.expectedLinkAccess)
 			assert.Check(t, cmd.timeout == test.expectedTimeout)
 			assert.Check(t, cmd.status == test.expectedStatus)
@@ -225,7 +198,6 @@ func TestCmdSiteCreate_Run(t *testing.T) {
 		siteName           string
 		serviceAccountName string
 		options            map[string]string
-		output             string
 		errorMessage       string
 	}
 
@@ -266,20 +238,8 @@ func TestCmdSiteCreate_Run(t *testing.T) {
 			siteName:           "test",
 			serviceAccountName: "my-service-account",
 			options:            map[string]string{"name": "my-site"},
-			output:             "yaml",
 			skupperError:       "",
 		},
-		{
-			name:               "run fails because the output format is not supported",
-			k8sObjects:         nil,
-			skupperObjects:     nil,
-			siteName:           "test",
-			serviceAccountName: "my-service-account",
-			options:            map[string]string{"name": "my-site"},
-			output:             "unsupported",
-			skupperError:       "",
-			errorMessage:       "format unsupported not supported",
-		},
 	}
 
 	for _, test := range testTable {
@@ -293,7 +253,6 @@ func TestCmdSiteCreate_Run(t *testing.T) {
 
 		command.siteName = test.siteName
 		command.serviceAccountName = test.serviceAccountName
-		command.output = test.output
 
 		t.Run(test.name, func(t *testing.T) {
 
@@ -314,7 +273,6 @@ func TestCmdSiteCreate_WaitUntil(t *testing.T) {
 		skupperObjects []runtime.Object
 		skupperError   string
 		expectError    bool
-		output         string
 	}
 
 	testTable := []test{
@@ -351,26 +309,6 @@ func TestCmdSiteCreate_WaitUntil(t *testing.T) {
 			skupperError: "it failed",
 			expectError:  true,
 		},
-		{
-			name:       "there is no need to wait for a site, the user just wanted the output",
-			k8sObjects: nil,
-			skupperObjects: []runtime.Object{
-				&v2alpha1.Site{
-					ObjectMeta: v1.ObjectMeta{
-						Name:      "my-site",
-						Namespace: "test",
-					},
-					Status: v2alpha1.SiteStatus{
-						Status: v2alpha1.Status{
-							Message: "OK",
-						},
-					},
-				},
-			},
-			output:       "yaml",
-			skupperError: "",
-			expectError:  false,
-		},
 		{
 			name:       "site is ready",
 			status:     "ready",
@@ -495,7 +433,6 @@ func TestCmdSiteCreate_WaitUntil(t *testing.T) {
 		assert.Assert(t, err)
 		command.Client = fakeSkupperClient.GetSkupperClient().SkupperV2alpha1()
 		command.siteName = "my-site"
-		command.output = test.output
 		command.timeout = time.Second
 		command.status = test.status
 
diff --git a/internal/cmd/skupper/site/kube/site_generate_test.go b/internal/cmd/skupper/site/kube/site_generate_test.go
new file mode 100644
index 00000000..e12d7e6f
--- /dev/null
+++ b/internal/cmd/skupper/site/kube/site_generate_test.go
@@ -0,0 +1,236 @@
+package kube
+
+import (
+	"testing"
+
+	"github.com/skupperproject/skupper/api/types"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/testutils"
+	fakeclient "github.com/skupperproject/skupper/internal/kube/client/fake"
+
+	"gotest.tools/v3/assert"
+	"k8s.io/apimachinery/pkg/runtime"
+)
+
+func TestCmdSiteGenerate_ValidateInput(t *testing.T) {
+	type test struct {
+		name           string
+		args           []string
+		k8sObjects     []runtime.Object
+		skupperObjects []runtime.Object
+		flags          *common.CommandSiteGenerateFlags
+		expectedError  string
+	}
+
+	testTable := []test{
+		{
+			name:          "site name is not valid.",
+			args:          []string{"my new site"},
+			expectedError: "site name is not valid: value does not match this regular expression: ^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$",
+		},
+		{
+			name:          "site name is not specified.",
+			args:          []string{},
+			expectedError: "site name must not be empty",
+		},
+		{
+			name:          "more than one argument was specified",
+			args:          []string{"my", "site"},
+			expectedError: "only one argument is allowed for this command.",
+		},
+		{
+			name:          "service account name is not valid.",
+			args:          []string{"my-site"},
+			flags:         &common.CommandSiteGenerateFlags{ServiceAccount: "not valid service account name"},
+			expectedError: "service account name is not valid: value does not match this regular expression: ^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$",
+		},
+		{
+			name:  "link access type is not valid",
+			args:  []string{"my-site"},
+			flags: &common.CommandSiteGenerateFlags{LinkAccessType: "not-valid"},
+			expectedError: "link access type is not valid: value not-valid not allowed. It should be one of this options: [route loadbalancer default]\n" +
+				"for the site to work with this type of linkAccess, the --enable-link-access option must be set to true",
+		},
+		{
+			name:          "output format is not valid",
+			args:          []string{"my-site"},
+			flags:         &common.CommandSiteGenerateFlags{Output: "not-valid"},
+			expectedError: "format value not-valid not allowed. It should be one of this options: [json yaml]",
+		},
+		{
+			name:  "bind-host flag is not valid for this platform",
+			args:  []string{"my-site"},
+			flags: &common.CommandSiteGenerateFlags{BindHost: "host"},
+		},
+		{
+			name:  "subject alternative names flag is not valid for this platform",
+			args:  []string{"my-site"},
+			flags: &common.CommandSiteGenerateFlags{SubjectAlternativeNames: []string{"test"}},
+		},
+	}
+
+	for _, test := range testTable {
+		t.Run(test.name, func(t *testing.T) {
+			command := &CmdSiteGenerate{
+				Namespace: "test",
+			}
+
+			cmd := common.ConfigureCobraCommand(types.PlatformKubernetes, common.SkupperCmdDescription{}, command, nil)
+
+			command.CobraCmd = cmd
+
+			if test.flags != nil {
+				command.Flags = test.flags
+			}
+
+			testutils.CheckValidateInput(t, command, test.expectedError, test.args)
+
+		})
+	}
+}
+
+func TestCmdSiteGenerate_InputToOptions(t *testing.T) {
+
+	type test struct {
+		name               string
+		args               []string
+		flags              common.CommandSiteGenerateFlags
+		expectedLinkAccess string
+		expectedOutput     string
+	}
+
+	testTable := []test{
+		{
+			name:               "options without link access enabled",
+			args:               []string{"my-site"},
+			flags:              common.CommandSiteGenerateFlags{},
+			expectedLinkAccess: "",
+			expectedOutput:     "yaml",
+		},
+		{
+			name:               "options with link access enabled but using a type by default",
+			args:               []string{"my-site"},
+			flags:              common.CommandSiteGenerateFlags{EnableLinkAccess: true},
+			expectedLinkAccess: "default",
+			expectedOutput:     "yaml",
+		},
+		{
+			name:               "options with link access enabled using the nodeport type",
+			args:               []string{"my-site"},
+			flags:              common.CommandSiteGenerateFlags{EnableLinkAccess: true, LinkAccessType: "nodeport"},
+			expectedLinkAccess: "nodeport",
+			expectedOutput:     "yaml",
+		},
+		{
+			name:               "options with link access options not well specified",
+			args:               []string{"my-site"},
+			flags:              common.CommandSiteGenerateFlags{EnableLinkAccess: false, LinkAccessType: "nodeport"},
+			expectedLinkAccess: "",
+			expectedOutput:     "yaml",
+		},
+		{
+			name:               "options output type",
+			args:               []string{"my-site"},
+			flags:              common.CommandSiteGenerateFlags{EnableLinkAccess: false, LinkAccessType: "nodeport", Output: "json"},
+			expectedLinkAccess: "",
+			expectedOutput:     "json",
+		},
+	}
+
+	for _, test := range testTable {
+		t.Run(test.name, func(t *testing.T) {
+			cmd, err := newCmdSiteGenerateWithMocks("test")
+			assert.Assert(t, err)
+			cmd.Flags = &test.flags
+			cmd.siteName = "my-site"
+
+			cmd.InputToOptions()
+
+			assert.Check(t, cmd.output == test.expectedOutput)
+			assert.Check(t, cmd.linkAccessType == test.expectedLinkAccess)
+		})
+	}
+}
+
+func TestCmdSiteGenerate_Run(t *testing.T) {
+	type test struct {
+		name               string
+		k8sObjects         []runtime.Object
+		skupperObjects     []runtime.Object
+		skupperError       string
+		siteName           string
+		serviceAccountName string
+		options            map[string]string
+		output             string
+		errorMessage       string
+	}
+
+	testTable := []test{
+		{
+			name:               "runs ok",
+			k8sObjects:         nil,
+			skupperObjects:     nil,
+			siteName:           "my-site",
+			serviceAccountName: "my-service-account",
+			options:            map[string]string{"name": "my-site"},
+			skupperError:       "",
+			output:             "yaml",
+		},
+		{
+			name:               "runs ok with yaml output",
+			k8sObjects:         nil,
+			skupperObjects:     nil,
+			siteName:           "test",
+			serviceAccountName: "my-service-account",
+			options:            map[string]string{"name": "my-site"},
+			output:             "json",
+			skupperError:       "",
+		},
+		{
+			name:               "runs fails because the output format is not supported",
+			k8sObjects:         nil,
+			skupperObjects:     nil,
+			siteName:           "test",
+			serviceAccountName: "my-service-account",
+			options:            map[string]string{"name": "my-site"},
+			output:             "unsupported",
+			skupperError:       "",
+			errorMessage:       "format unsupported not supported",
+		},
+	}
+
+	for _, test := range testTable {
+		command := &CmdSiteGenerate{
+			Namespace: "test",
+		}
+
+		fakeSkupperClient, err := fakeclient.NewFakeClient(command.Namespace, test.k8sObjects, test.skupperObjects, test.skupperError)
+		assert.Assert(t, err)
+		command.Client = fakeSkupperClient.GetSkupperClient().SkupperV2alpha1()
+
+		command.siteName = test.siteName
+		command.serviceAccountName = test.serviceAccountName
+		command.output = test.output
+
+		t.Run(test.name, func(t *testing.T) {
+
+			err := command.Run()
+			if err != nil {
+				assert.Check(t, test.errorMessage == err.Error())
+			} else {
+				assert.Check(t, err == nil)
+			}
+		})
+	}
+}
+
+// --- helper methods
+
+func newCmdSiteGenerateWithMocks(namespace string) (*CmdSiteGenerate, error) {
+
+	cmdSiteGenerate := &CmdSiteGenerate{
+		Namespace: namespace,
+	}
+
+	return cmdSiteGenerate, nil
+}
diff --git a/internal/cmd/skupper/site/kube/site_update_test.go b/internal/cmd/skupper/site/kube/site_update_test.go
index 9c8a3fbc..29faf20c 100644
--- a/internal/cmd/skupper/site/kube/site_update_test.go
+++ b/internal/cmd/skupper/site/kube/site_update_test.go
@@ -113,7 +113,7 @@ func TestCmdSiteUpdate_ValidateInput(t *testing.T) {
 			expectedError: "service account name is not valid: serviceaccounts \"not valid service account name\" not found",
 		},
 		{
-			name:  "host name was specified, but this flag does not work on kube platforms",
+			name:  "bind-host name was specified, but this flag does not work on kube platforms",
 			args:  []string{"my-site"},
 			flags: &common.CommandSiteUpdateFlags{BindHost: "host", Timeout: time.Minute},
 			skupperObjects: []runtime.Object{
@@ -129,7 +129,7 @@ func TestCmdSiteUpdate_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			expectedError: "--host flag is not supported on this platform",
+			expectedError: "--bind-host flag is not supported on this platform",
 		},
 		{
 			name:       "link access type is not valid",
@@ -153,26 +153,6 @@ func TestCmdSiteUpdate_ValidateInput(t *testing.T) {
 			expectedError: "link access type is not valid: value not-valid not allowed. It should be one of this options: [route loadbalancer default]\n" +
 				"for the site to work with this type of linkAccess, the --enable-link-access option must be set to true",
 		},
-		{
-			name:       "output format is not valid",
-			args:       []string{"my-site"},
-			flags:      &common.CommandSiteUpdateFlags{Output: "not-valid", Timeout: time.Minute},
-			k8sObjects: nil,
-			skupperObjects: []runtime.Object{
-				&v2alpha1.Site{
-					ObjectMeta: v1.ObjectMeta{
-						Name:      "my-site",
-						Namespace: "test",
-					},
-					Status: v2alpha1.SiteStatus{
-						Status: v2alpha1.Status{
-							Message: "OK",
-						},
-					},
-				},
-			},
-			expectedError: "output type is not valid: value not-valid not allowed. It should be one of this options: [json yaml]",
-		},
 		{
 			name:           "there is no skupper site",
 			args:           []string{"my-site"},
@@ -369,7 +349,6 @@ func TestCmdSiteUpdate_InputToOptions(t *testing.T) {
 		args               []string
 		flags              common.CommandSiteUpdateFlags
 		expectedLinkAccess string
-		expectedOutput     string
 		expectedTimeout    time.Duration
 		expectedStatus     string
 	}
@@ -379,36 +358,31 @@ func TestCmdSiteUpdate_InputToOptions(t *testing.T) {
 			name:               "options without link access enabled",
 			args:               []string{"my-site"},
 			flags:              common.CommandSiteUpdateFlags{},
-			expectedLinkAccess: "none",
-			expectedOutput:     "",
+			expectedLinkAccess: "",
 		},
 		{
 			name:               "options with link access enabled but using a type by default and link access host specified",
 			args:               []string{"my-site"},
 			flags:              common.CommandSiteUpdateFlags{EnableLinkAccess: true},
-			expectedLinkAccess: "loadbalancer",
-			expectedOutput:     "",
+			expectedLinkAccess: "default",
 		},
 		{
 			name:               "options with link access enabled using the nodeport type",
 			args:               []string{"my-site"},
 			flags:              common.CommandSiteUpdateFlags{EnableLinkAccess: true, LinkAccessType: "nodeport"},
 			expectedLinkAccess: "nodeport",
-			expectedOutput:     "",
 		},
 		{
 			name:               "options with link access options not well specified",
 			args:               []string{"my-site"},
 			flags:              common.CommandSiteUpdateFlags{EnableLinkAccess: false, LinkAccessType: "nodeport"},
-			expectedLinkAccess: "none",
-			expectedOutput:     "",
+			expectedLinkAccess: "",
 		},
 		{
-			name:               "options with output type and timeout",
+			name:               "options with loadbalancer and timeout",
 			args:               []string{"my-site"},
-			flags:              common.CommandSiteUpdateFlags{EnableLinkAccess: false, LinkAccessType: "nodeport", Output: "yaml", Timeout: time.Minute},
-			expectedLinkAccess: "none",
-			expectedOutput:     "yaml",
+			flags:              common.CommandSiteUpdateFlags{EnableLinkAccess: true, LinkAccessType: "loadbalancer", Timeout: time.Minute},
+			expectedLinkAccess: "loadbalancer",
 			expectedTimeout:    time.Minute,
 		},
 		{
@@ -433,8 +407,9 @@ func TestCmdSiteUpdate_InputToOptions(t *testing.T) {
 
 			command.InputToOptions()
 
-			assert.Check(t, command.output == test.expectedOutput)
 			assert.Check(t, command.status == test.expectedStatus)
+			assert.Check(t, command.linkAccessType == test.expectedLinkAccess)
+			assert.Check(t, command.timeout == test.expectedTimeout)
 		})
 	}
 }
@@ -448,7 +423,6 @@ func TestCmdSiteUpdate_Run(t *testing.T) {
 		siteName           string
 		serviceAccountName string
 		linkAccessType     string
-		output             string
 		errorMessage       string
 	}
 
@@ -467,7 +441,6 @@ func TestCmdSiteUpdate_Run(t *testing.T) {
 			siteName:           "my-site",
 			serviceAccountName: "my-service-account",
 			linkAccessType:     "default",
-			output:             "",
 			skupperError:       "",
 			errorMessage:       "",
 		},
@@ -478,7 +451,6 @@ func TestCmdSiteUpdate_Run(t *testing.T) {
 			siteName:           "my-site",
 			serviceAccountName: "my-service-account",
 			linkAccessType:     "default",
-			output:             "",
 			skupperError:       "error",
 			errorMessage:       "error",
 		},
@@ -489,18 +461,6 @@ func TestCmdSiteUpdate_Run(t *testing.T) {
 			siteName:           "my-site",
 			serviceAccountName: "my-service-account",
 			linkAccessType:     "default",
-			output:             "yaml",
-			skupperError:       "",
-			errorMessage:       "sites.skupper.io \"my-site\" not found",
-		},
-		{
-			name:               "runs fails because the output format is not supported",
-			k8sObjects:         nil,
-			skupperObjects:     nil,
-			siteName:           "my-site",
-			serviceAccountName: "my-service-account",
-			linkAccessType:     "default",
-			output:             "unsupported",
 			skupperError:       "",
 			errorMessage:       "sites.skupper.io \"my-site\" not found",
 		},
@@ -517,7 +477,6 @@ func TestCmdSiteUpdate_Run(t *testing.T) {
 		command.siteName = test.siteName
 		command.serviceAccountName = test.serviceAccountName
 		command.linkAccessType = test.linkAccessType
-		command.output = test.output
 
 		t.Run(test.name, func(t *testing.T) {
 
diff --git a/internal/cmd/skupper/site/nonkube/site_create_test.go b/internal/cmd/skupper/site/nonkube/site_create_test.go
index 3f1f36ce..82d03958 100644
--- a/internal/cmd/skupper/site/nonkube/site_create_test.go
+++ b/internal/cmd/skupper/site/nonkube/site_create_test.go
@@ -41,12 +41,6 @@ func TestNonKubeCmdSiteCreate_ValidateInput(t *testing.T) {
 			flags:         &common.CommandSiteCreateFlags{BindHost: "bindhost"},
 			expectedError: "only one argument is allowed for this command",
 		},
-		{
-			name:          "output format is not valid",
-			args:          []string{"my-site"},
-			flags:         &common.CommandSiteCreateFlags{BindHost: "127.0.0.1", Output: "not-valid"},
-			expectedError: "output type is not valid: value not-valid not allowed. It should be one of this options: [json yaml]",
-		},
 		{
 			name:          "bindHost was not specified ok",
 			args:          []string{"my-site"},
@@ -79,7 +73,6 @@ func TestNonKubeCmdSiteCreate_ValidateInput(t *testing.T) {
 			name: "flags all valid",
 			args: []string{"my-site"},
 			flags: &common.CommandSiteCreateFlags{
-				Output:                  "json",
 				BindHost:                "1.2.3.4",
 				EnableLinkAccess:        true,
 				SubjectAlternativeNames: []string{"3.3.3.3"},
@@ -118,7 +111,6 @@ func TestNonKubeCmdSiteCreate_InputToOptions(t *testing.T) {
 		flags                           common.CommandSiteCreateFlags
 		expectedSettings                map[string]string
 		expectedLinkAccess              bool
-		expectedOutput                  string
 		expectedNamespace               string
 		expectedSubjectAlternativeNames []string
 		expectedBindHost                string
@@ -134,7 +126,6 @@ func TestNonKubeCmdSiteCreate_InputToOptions(t *testing.T) {
 				"name": "my-site",
 			},
 			expectedLinkAccess:              false,
-			expectedOutput:                  "",
 			expectedNamespace:               "default",
 			expectedBindHost:                "",
 			expectedRouterAccessName:        "",
@@ -148,26 +139,11 @@ func TestNonKubeCmdSiteCreate_InputToOptions(t *testing.T) {
 				"name": "my-site",
 			},
 			expectedLinkAccess:              true,
-			expectedOutput:                  "",
 			expectedNamespace:               "default",
 			expectedBindHost:                "test",
 			expectedRouterAccessName:        "router-access-my-site",
 			expectedSubjectAlternativeNames: nil,
 		},
-		{
-			name:  "options output type",
-			args:  []string{"my-site"},
-			flags: common.CommandSiteCreateFlags{EnableLinkAccess: false, BindHost: "test", Output: "yaml"},
-			expectedSettings: map[string]string{
-				"name": "my-site",
-			},
-			expectedLinkAccess:              false,
-			expectedOutput:                  "yaml",
-			expectedNamespace:               "default",
-			expectedBindHost:                "",
-			expectedSubjectAlternativeNames: nil,
-			expectedRouterAccessName:        "",
-		},
 		{
 			name:      "options with subject alternative names",
 			args:      []string{"my-site"},
@@ -210,7 +186,6 @@ func TestNonKubeCmdSiteCreate_InputToOptions(t *testing.T) {
 			cmd.InputToOptions()
 
 			assert.DeepEqual(t, cmd.options, test.expectedSettings)
-			assert.Check(t, cmd.output == test.expectedOutput)
 			assert.Check(t, cmd.namespace == test.expectedNamespace)
 			assert.Check(t, cmd.bindHost == test.expectedBindHost)
 			assert.Check(t, cmd.linkAccessEnabled == test.expectedLinkAccess)
@@ -229,7 +204,6 @@ func TestNonKubeCmdSiteCreate_Run(t *testing.T) {
 		siteName          string
 		host              string
 		options           map[string]string
-		output            string
 		errorMessage      string
 		routerAccessName  string
 		linkAccessEnabled bool
@@ -247,27 +221,13 @@ func TestNonKubeCmdSiteCreate_Run(t *testing.T) {
 			options:           map[string]string{"name": "my-site"},
 		},
 		{
-			name:              "runs ok without create site",
-			k8sObjects:        nil,
-			skupperObjects:    nil,
-			siteName:          "my-site",
-			host:              "bindhost",
-			routerAccessName:  "ra-test",
-			options:           map[string]string{"name": "my-site"},
-			output:            "yaml",
-			linkAccessEnabled: true,
-			skupperError:      "",
-		},
-		{
-			name:           "runs fails because the output format is not supported",
+			name:           "runs ok without create site",
 			k8sObjects:     nil,
 			skupperObjects: nil,
-			siteName:       "my-site",
-			host:           "bindHost",
+			siteName:       "test",
+			host:           "bindhost",
 			options:        map[string]string{"name": "my-site"},
-			output:         "unsupported",
 			skupperError:   "",
-			errorMessage:   "format unsupported not supported",
 		},
 	}
 
@@ -276,7 +236,6 @@ func TestNonKubeCmdSiteCreate_Run(t *testing.T) {
 
 		command.siteName = test.siteName
 		command.options = test.options
-		command.output = test.output
 		command.routerAccessName = test.routerAccessName
 		command.linkAccessEnabled = test.linkAccessEnabled
 		command.siteHandler = fs.NewSiteHandler(command.namespace)
diff --git a/internal/cmd/skupper/site/nonkube/site_generate_test.go b/internal/cmd/skupper/site/nonkube/site_generate_test.go
new file mode 100644
index 00000000..43dc2a8d
--- /dev/null
+++ b/internal/cmd/skupper/site/nonkube/site_generate_test.go
@@ -0,0 +1,294 @@
+package nonkube
+
+import (
+	"testing"
+
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/testutils"
+	fs2 "github.com/skupperproject/skupper/internal/nonkube/client/fs"
+	"github.com/spf13/cobra"
+
+	"gotest.tools/v3/assert"
+	"k8s.io/apimachinery/pkg/runtime"
+)
+
+func TestNonKubeCmdSiteGenerate_ValidateInput(t *testing.T) {
+	type test struct {
+		name              string
+		args              []string
+		k8sObjects        []runtime.Object
+		skupperObjects    []runtime.Object
+		flags             *common.CommandSiteGenerateFlags
+		cobraGenericFlags map[string]string
+		expectedError     string
+	}
+
+	testTable := []test{
+		{
+			name:          "site name is not valid.",
+			args:          []string{"my new site"},
+			flags:         &common.CommandSiteGenerateFlags{BindHost: "bindhost"},
+			expectedError: "site name is not valid: value does not match this regular expression: ^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$",
+		},
+		{
+			name:          "site name is not specified.",
+			args:          []string{},
+			flags:         &common.CommandSiteGenerateFlags{},
+			expectedError: "site name must not be empty",
+		},
+		{
+			name:          "more than one argument was specified",
+			args:          []string{"my", "site"},
+			flags:         &common.CommandSiteGenerateFlags{BindHost: "1.2.3.4"},
+			expectedError: "only one argument is allowed for this command",
+		},
+		{
+			name:  "bindHost was not specified ok",
+			args:  []string{"my-site"},
+			flags: &common.CommandSiteGenerateFlags{EnableLinkAccess: true},
+		},
+		{
+			name:          "bindHost was not valid",
+			args:          []string{"my-site"},
+			flags:         &common.CommandSiteGenerateFlags{EnableLinkAccess: true, BindHost: "not-valid$"},
+			expectedError: "bindhost is not valid: a valid IP address or hostname is expected",
+		},
+		{
+			name:          "subjectAlternativeNames was not valid",
+			args:          []string{"my-site"},
+			flags:         &common.CommandSiteGenerateFlags{EnableLinkAccess: true, BindHost: "bindhost", SubjectAlternativeNames: []string{"not-valid$"}},
+			expectedError: "SubjectAlternativeNames is not valid: a valid IP address or hostname is expected",
+		},
+		{
+			name:          "output format is not valid",
+			args:          []string{"my-site"},
+			flags:         &common.CommandSiteGenerateFlags{BindHost: "bindhost", Output: "not-valid"},
+			expectedError: "output type is not valid: value not-valid not allowed. It should be one of this options: [json yaml]",
+		},
+		{
+			name:  "service-account is not valid on this platform",
+			args:  []string{"my-site"},
+			flags: &common.CommandSiteGenerateFlags{ServiceAccount: "service-account"},
+		},
+		{
+			name:  "kubernetes flags are not valid on this platform",
+			args:  []string{"my-site"},
+			flags: &common.CommandSiteGenerateFlags{},
+			cobraGenericFlags: map[string]string{
+				common.FlagNameContext:    "test",
+				common.FlagNameKubeconfig: "test",
+			},
+		},
+		{
+			name: "flags all valid",
+			args: []string{"my-site"},
+			flags: &common.CommandSiteGenerateFlags{
+				BindHost:                "1.2.3.4",
+				EnableLinkAccess:        true,
+				SubjectAlternativeNames: []string{"3.3.3.3"},
+			},
+		},
+	}
+
+	for _, test := range testTable {
+		t.Run(test.name, func(t *testing.T) {
+			command := &CmdSiteGenerate{Flags: &common.CommandSiteGenerateFlags{}}
+			command.CobraCmd = &cobra.Command{Use: "test"}
+
+			if test.flags != nil {
+				command.Flags = test.flags
+			}
+
+			if test.cobraGenericFlags != nil && len(test.cobraGenericFlags) > 0 {
+				for name, value := range test.cobraGenericFlags {
+					command.CobraCmd.Flags().String(name, value, "")
+				}
+			}
+
+			testutils.CheckValidateInput(t, command, test.expectedError, test.args)
+
+		})
+	}
+}
+
+func TestNonKubeCmdSiteGenerate_InputToOptions(t *testing.T) {
+
+	type test struct {
+		name                            string
+		args                            []string
+		namespace                       string
+		flags                           common.CommandSiteGenerateFlags
+		expectedSettings                map[string]string
+		expectedLinkAccess              bool
+		expectedOutput                  string
+		expectedNamespace               string
+		expectedBindHost                string
+		expectedSubjectAlternativeNames []string
+		expectedRouterAccessName        string
+	}
+
+	testTable := []test{
+		{
+			name:  "options without link access enabled",
+			args:  []string{"my-site"},
+			flags: common.CommandSiteGenerateFlags{},
+			expectedSettings: map[string]string{
+				"name": "my-site",
+			},
+			expectedLinkAccess:       false,
+			expectedOutput:           "",
+			expectedBindHost:         "",
+			expectedRouterAccessName: "",
+			expectedNamespace:        "default",
+		},
+		{
+			name:  "options with link access enabled",
+			args:  []string{"my-site"},
+			flags: common.CommandSiteGenerateFlags{EnableLinkAccess: true, BindHost: "test"},
+			expectedSettings: map[string]string{
+				"name": "my-site",
+			},
+			expectedLinkAccess:              true,
+			expectedNamespace:               "default",
+			expectedBindHost:                "test",
+			expectedRouterAccessName:        "router-access-my-site",
+			expectedSubjectAlternativeNames: nil,
+		},
+		{
+			name:      "options with subject alternative names",
+			args:      []string{"my-site"},
+			namespace: "test",
+			flags:     common.CommandSiteGenerateFlags{BindHost: "1.2.3.4", SubjectAlternativeNames: []string{"test"}},
+			expectedSettings: map[string]string{
+				"name": "my-site",
+			},
+			expectedLinkAccess:              false,
+			expectedNamespace:               "test",
+			expectedBindHost:                "",
+			expectedSubjectAlternativeNames: nil,
+			expectedRouterAccessName:        "",
+		},
+		{
+			name:      "options with enable link access and subject alternative names",
+			args:      []string{"my-site"},
+			namespace: "test",
+			flags:     common.CommandSiteGenerateFlags{EnableLinkAccess: true, BindHost: "1.2.3.4", SubjectAlternativeNames: []string{"test"}},
+			expectedSettings: map[string]string{
+				"name": "my-site",
+			},
+			expectedLinkAccess:              true,
+			expectedNamespace:               "test",
+			expectedSubjectAlternativeNames: []string{"test"},
+			expectedBindHost:                "1.2.3.4",
+			expectedRouterAccessName:        "router-access-my-site",
+		},
+		{
+			name:  "options output type",
+			args:  []string{"my-site"},
+			flags: common.CommandSiteGenerateFlags{EnableLinkAccess: false, Output: "yaml"},
+			expectedSettings: map[string]string{
+				"name": "my-site",
+			},
+			expectedLinkAccess: false,
+			expectedOutput:     "yaml",
+			expectedNamespace:  "default",
+		},
+	}
+
+	for _, test := range testTable {
+		t.Run(test.name, func(t *testing.T) {
+			cmd := CmdSiteGenerate{}
+			cmd.Flags = &test.flags
+			cmd.siteName = "my-site"
+			cmd.namespace = test.namespace
+			cmd.siteHandler = fs2.NewSiteHandler(cmd.namespace)
+			cmd.routerAccessHandler = fs2.NewRouterAccessHandler(cmd.namespace)
+
+			cmd.InputToOptions()
+
+			assert.DeepEqual(t, cmd.options, test.expectedSettings)
+
+			assert.Check(t, cmd.output == test.expectedOutput)
+			assert.DeepEqual(t, cmd.options, test.expectedSettings)
+			assert.Check(t, cmd.namespace == test.expectedNamespace)
+			assert.Check(t, cmd.bindHost == test.expectedBindHost)
+			assert.Check(t, cmd.linkAccessEnabled == test.expectedLinkAccess)
+			assert.Check(t, cmd.routerAccessName == test.expectedRouterAccessName)
+			assert.DeepEqual(t, cmd.subjectAlternativeNames, test.expectedSubjectAlternativeNames)
+		})
+	}
+}
+
+func TestNonKubeCmdSiteGenerate_Run(t *testing.T) {
+	type test struct {
+		name              string
+		k8sObjects        []runtime.Object
+		skupperObjects    []runtime.Object
+		skupperError      string
+		siteName          string
+		host              string
+		options           map[string]string
+		output            string
+		errorMessage      string
+		routerAccessName  string
+		linkAccessEnabled bool
+	}
+
+	testTable := []test{
+		{
+			name:              "runs ok",
+			k8sObjects:        nil,
+			skupperObjects:    nil,
+			siteName:          "my-site",
+			host:              "bindHost",
+			routerAccessName:  "ra-test",
+			linkAccessEnabled: true,
+			options:           map[string]string{"name": "my-site"},
+			output:            "json",
+		},
+		{
+			name:           "runs ok with yaml output",
+			k8sObjects:     nil,
+			skupperObjects: nil,
+			siteName:       "test",
+			host:           "bindhost",
+			options:        map[string]string{"name": "my-site"},
+			output:         "yaml",
+			skupperError:   "",
+		},
+		{
+			name:           "runs fails because the output format is not supported",
+			k8sObjects:     nil,
+			skupperObjects: nil,
+			siteName:       "test",
+			host:           "bindhost",
+			options:        map[string]string{"name": "my-site"},
+			output:         "unsupported",
+			skupperError:   "",
+			errorMessage:   "format unsupported not supported",
+		},
+	}
+
+	for _, test := range testTable {
+		command := &CmdSiteGenerate{}
+
+		command.siteName = test.siteName
+		command.options = test.options
+		command.output = test.output
+		command.routerAccessName = test.routerAccessName
+		command.linkAccessEnabled = test.linkAccessEnabled
+		command.siteHandler = fs2.NewSiteHandler(command.namespace)
+		command.routerAccessHandler = fs2.NewRouterAccessHandler(command.namespace)
+		defer command.siteHandler.Delete("my-site")
+		defer command.routerAccessHandler.Delete("my-site")
+		t.Run(test.name, func(t *testing.T) {
+
+			err := command.Run()
+			if err != nil {
+				assert.Check(t, test.errorMessage == err.Error(), err.Error())
+			} else {
+				assert.Check(t, err == nil)
+			}
+		})
+	}
+}
diff --git a/internal/cmd/skupper/site/nonkube/site_update_test.go b/internal/cmd/skupper/site/nonkube/site_update_test.go
index ed480bee..fb230faf 100644
--- a/internal/cmd/skupper/site/nonkube/site_update_test.go
+++ b/internal/cmd/skupper/site/nonkube/site_update_test.go
@@ -74,12 +74,6 @@ func TestCmdSiteUpdate_ValidateInput(t *testing.T) {
 			flags:         &common.CommandSiteUpdateFlags{SubjectAlternativeNames: []string{"not-valid$"}},
 			expectedError: "SubjectAlternativeNames are not valid: a valid IP address or hostname is expected",
 		},
-		{
-			name:          "output is not valid",
-			args:          []string{"my-site"},
-			flags:         &common.CommandSiteUpdateFlags{Output: "not-supported"},
-			expectedError: "output type is not valid: value not-supported not allowed. It should be one of this options: [json yaml]",
-		},
 		{
 			name:  "kubernetes flags are not valid on this platform",
 			args:  []string{"my-site"},
@@ -94,7 +88,6 @@ func TestCmdSiteUpdate_ValidateInput(t *testing.T) {
 			name: "flags all valid",
 			args: []string{"my-site"},
 			flags: &common.CommandSiteUpdateFlags{
-				Output:                  "json",
 				BindHost:                "1.2.3.4",
 				EnableLinkAccess:        true,
 				SubjectAlternativeNames: []string{"3.3.3.3"},
@@ -308,7 +301,6 @@ func TestCmdSiteUpdate_Run(t *testing.T) {
 				BindHost:                "1.2.3.4",
 				EnableLinkAccess:        true,
 				SubjectAlternativeNames: []string{"2.2.2.2", "test", "5.6.7.8"},
-				Output:                  "json",
 			},
 			linkAccessEnabled: true,
 		},
@@ -317,7 +309,6 @@ func TestCmdSiteUpdate_Run(t *testing.T) {
 			siteName: "my-site",
 			flags: common.CommandSiteUpdateFlags{
 				EnableLinkAccess: false,
-				Output:           "yaml",
 			},
 		},
 	}
diff --git a/internal/cmd/skupper/site/site_test.go b/internal/cmd/skupper/site/site_test.go
index 1ba6c75f..f68342a0 100644
--- a/internal/cmd/skupper/site/site_test.go
+++ b/internal/cmd/skupper/site/site_test.go
@@ -25,7 +25,6 @@ func TestCmdSiteFactory(t *testing.T) {
 				common.FlagNameEnableLinkAccess:        "false",
 				common.FlagNameLinkAccessType:          "",
 				common.FlagNameServiceAccount:          "",
-				common.FlagNameOutput:                  "",
 				common.FlagNameTimeout:                 "3m0s",
 				common.FlagNameBindHost:                "0.0.0.0",
 				common.FlagNameSubjectAlternativeNames: "[]",
@@ -39,7 +38,6 @@ func TestCmdSiteFactory(t *testing.T) {
 				common.FlagNameEnableLinkAccess:        "false",
 				common.FlagNameLinkAccessType:          "",
 				common.FlagNameServiceAccount:          "",
-				common.FlagNameOutput:                  "",
 				common.FlagNameBindHost:                "",
 				common.FlagNameTimeout:                 "30s",
 				common.FlagNameSubjectAlternativeNames: "[]",
@@ -63,6 +61,18 @@ func TestCmdSiteFactory(t *testing.T) {
 			},
 			command: CmdSiteStatusFactory(types.PlatformKubernetes),
 		},
+		{
+			name: "CmdSiteGenerateFactory",
+			expectedFlagsWithDefaultValue: map[string]interface{}{
+				common.FlagNameEnableLinkAccess:        "false",
+				common.FlagNameLinkAccessType:          "",
+				common.FlagNameServiceAccount:          "",
+				common.FlagNameOutput:                  "yaml",
+				common.FlagNameBindHost:                "0.0.0.0",
+				common.FlagNameSubjectAlternativeNames: "[]",
+			},
+			command: CmdSiteGenerateFactory(types.PlatformKubernetes),
+		},
 	}
 
 	for _, test := range testTable {