Repository: skupperproject/skupper
Pull request #1608 (2024-08-21 16:00:03)

[v2] The CLI exits with success on an unknown command
**Description of the issue**

```
~/code/skupper-example-hello-world$ skupper init
Error: unknown command "init" for "skupper"
Run 'skupper --help' for usage.
unknown command "init" for "skupper"

~/code/skupper-example-hello-world$ echo $?
0 
```
It should instead exit with an error code.

**Description of the solution**
When handling an error by default the exit command will be `1`.
When handling command validation errors the exit command will be `2`.

Fixes: #1498 


Patch:
diff --git a/internal/cmd/skupper/connector/kube/connector_create.go b/internal/cmd/skupper/connector/kube/connector_create.go
index 2a958ad8..5513bbde 100644
--- a/internal/cmd/skupper/connector/kube/connector_create.go
+++ b/internal/cmd/skupper/connector/kube/connector_create.go
@@ -253,7 +253,7 @@ func (cmd *CmdConnectorCreate) WaitUntil() error {
 	})
 
 	if err != nil {
-		return fmt.Errorf("Connector %q not ready yet, check the logs for more information\n", cmd.name)
+		return fmt.Errorf("Connector %q not ready yet, check the status for more information\n", cmd.name)
 	}
 
 	fmt.Printf("Connector %q is ready\n", cmd.name)
diff --git a/internal/cmd/skupper/connector/kube/connector_delete.go b/internal/cmd/skupper/connector/kube/connector_delete.go
index beaeff66..c199bb60 100644
--- a/internal/cmd/skupper/connector/kube/connector_delete.go
+++ b/internal/cmd/skupper/connector/kube/connector_delete.go
@@ -119,7 +119,7 @@ func (cmd *CmdConnectorDelete) WaitUntil() error {
 	})
 
 	if err != nil {
-		return fmt.Errorf("Connector %q not deleted yet, check the logs for more information %s\n", cmd.name, err)
+		return fmt.Errorf("Connector %q not deleted yet, check the status for more information %s\n", cmd.name, err)
 	}
 
 	fmt.Printf("Connector %q deleted\n", cmd.name)
diff --git a/internal/cmd/skupper/connector/kube/connector_update.go b/internal/cmd/skupper/connector/kube/connector_update.go
index d8e2da63..0c605a54 100644
--- a/internal/cmd/skupper/connector/kube/connector_update.go
+++ b/internal/cmd/skupper/connector/kube/connector_update.go
@@ -262,7 +262,7 @@ func (cmd *CmdConnectorUpdate) WaitUntil() error {
 	})
 
 	if err != nil {
-		return fmt.Errorf("Connector %q not ready yet, check the logs for more information\n", cmd.name)
+		return fmt.Errorf("Connector %q not ready yet, check the status for more information\n", cmd.name)
 	}
 
 	fmt.Printf("Connector %q is ready\n", cmd.name)
diff --git a/internal/cmd/skupper/link/kube/link_delete.go b/internal/cmd/skupper/link/kube/link_delete.go
index 08d36bfe..b96578c8 100644
--- a/internal/cmd/skupper/link/kube/link_delete.go
+++ b/internal/cmd/skupper/link/kube/link_delete.go
@@ -123,7 +123,7 @@ func (cmd *CmdLinkDelete) WaitUntil() error {
 	})
 
 	if err != nil {
-		return fmt.Errorf("Link %q not deleted yet, check the logs for more information\n", cmd.linkName)
+		return fmt.Errorf("Link %q not deleted yet, check the status for more information\n", cmd.linkName)
 	}
 
 	fmt.Printf("Link %q is deleted\n", cmd.linkName)
diff --git a/internal/cmd/skupper/link/kube/link_generate.go b/internal/cmd/skupper/link/kube/link_generate.go
index ef464090..0d2c1276 100644
--- a/internal/cmd/skupper/link/kube/link_generate.go
+++ b/internal/cmd/skupper/link/kube/link_generate.go
@@ -280,7 +280,7 @@ func (cmd *CmdLinkGenerate) WaitUntil() error {
 		})
 
 		if err != nil {
-			return fmt.Errorf("TLS secret %q not ready yet, check the logs for more information\n", cmd.tlsSecret)
+			return fmt.Errorf("TLS secret %q not ready yet, check the status for more information\n", cmd.tlsSecret)
 		}
 
 	}
diff --git a/internal/cmd/skupper/link/kube/link_update.go b/internal/cmd/skupper/link/kube/link_update.go
index 4472b61b..ac234971 100644
--- a/internal/cmd/skupper/link/kube/link_update.go
+++ b/internal/cmd/skupper/link/kube/link_update.go
@@ -222,7 +222,7 @@ func (cmd *CmdLinkUpdate) WaitUntil() error {
 	})
 
 	if err != nil {
-		return fmt.Errorf("Link %q not updated yet, check the logs for more information\n", cmd.linkName)
+		return fmt.Errorf("Link %q not updated yet, check the status for more information\n", cmd.linkName)
 	}
 
 	fmt.Printf("Link %q is updated\n", cmd.linkName)
diff --git a/internal/cmd/skupper/listener/kube/listener_create.go b/internal/cmd/skupper/listener/kube/listener_create.go
index 53035df6..eb0a6cdd 100644
--- a/internal/cmd/skupper/listener/kube/listener_create.go
+++ b/internal/cmd/skupper/listener/kube/listener_create.go
@@ -230,7 +230,7 @@ func (cmd *CmdListenerCreate) WaitUntil() error {
 	})
 
 	if err != nil {
-		return fmt.Errorf("Listener %q not ready yet, check the logs for more information\n", cmd.name)
+		return fmt.Errorf("Listener %q not ready yet, check the status for more information\n", cmd.name)
 	}
 
 	fmt.Printf("Listener %q is ready\n", cmd.name)
diff --git a/internal/cmd/skupper/listener/kube/listener_delete.go b/internal/cmd/skupper/listener/kube/listener_delete.go
index fea5fdcc..158bf66d 100644
--- a/internal/cmd/skupper/listener/kube/listener_delete.go
+++ b/internal/cmd/skupper/listener/kube/listener_delete.go
@@ -120,7 +120,7 @@ func (cmd *CmdListenerDelete) WaitUntil() error {
 	})
 
 	if err != nil {
-		return fmt.Errorf("Listener %q not deleted yet, check the logs for more information %s\n", cmd.name, err)
+		return fmt.Errorf("Listener %q not deleted yet, check the status for more information %s\n", cmd.name, err)
 	}
 
 	fmt.Printf("Listener %q deleted\n", cmd.name)
diff --git a/internal/cmd/skupper/listener/kube/listener_update.go b/internal/cmd/skupper/listener/kube/listener_update.go
index 04cb62d7..ed3ad08b 100644
--- a/internal/cmd/skupper/listener/kube/listener_update.go
+++ b/internal/cmd/skupper/listener/kube/listener_update.go
@@ -231,7 +231,7 @@ func (cmd *CmdListenerUpdate) WaitUntil() error {
 	})
 
 	if err != nil {
-		return fmt.Errorf("Listener %q not ready yet, check the logs for more information\n", cmd.name)
+		return fmt.Errorf("Listener %q not ready yet, check the status for more information\n", cmd.name)
 	}
 
 	fmt.Printf("Listener %q is ready\n", cmd.name)
diff --git a/internal/cmd/skupper/site/kube/site_create.go b/internal/cmd/skupper/site/kube/site_create.go
index 24fa721b..575c2952 100644
--- a/internal/cmd/skupper/site/kube/site_create.go
+++ b/internal/cmd/skupper/site/kube/site_create.go
@@ -6,16 +6,14 @@ package kube
 import (
 	"context"
 	"fmt"
-	"k8s.io/client-go/kubernetes"
-
 	"github.com/skupperproject/skupper/internal/cmd/skupper/utils"
 	"github.com/skupperproject/skupper/internal/kube/client"
 	"github.com/skupperproject/skupper/pkg/apis/skupper/v1alpha1"
 	skupperv1alpha1 "github.com/skupperproject/skupper/pkg/generated/client/clientset/versioned/typed/skupper/v1alpha1"
-	"github.com/skupperproject/skupper/pkg/site"
 	"github.com/skupperproject/skupper/pkg/utils/validator"
 	"github.com/spf13/cobra"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/client-go/kubernetes"
 )
 
 var (
@@ -36,7 +34,6 @@ type CmdSiteCreate struct {
 	KubeClient         kubernetes.Interface
 	CobraCmd           cobra.Command
 	flags              CreateFlags
-	options            map[string]string
 	siteName           string
 	serviceAccountName string
 	Namespace          string
@@ -46,8 +43,7 @@ type CmdSiteCreate struct {
 
 func NewCmdSiteCreate() *CmdSiteCreate {
 
-	options := make(map[string]string)
-	skupperCmd := CmdSiteCreate{options: options, flags: CreateFlags{}}
+	skupperCmd := CmdSiteCreate{flags: CreateFlags{}}
 
 	cmd := cobra.Command{
 		Use:    "create <name>",
@@ -152,15 +148,8 @@ func (cmd *CmdSiteCreate) InputToOptions() {
 		} else {
 			cmd.linkAccessType = cmd.flags.linkAccessType
 		}
-	} else {
-		cmd.linkAccessType = "none"
 	}
 
-	options := make(map[string]string)
-	options[site.SiteConfigNameKey] = cmd.siteName
-
-	cmd.options = options
-
 	cmd.output = cmd.flags.output
 
 }
@@ -177,7 +166,6 @@ func (cmd *CmdSiteCreate) Run() error {
 			Namespace: cmd.Namespace,
 		},
 		Spec: v1alpha1.SiteSpec{
-			Settings:       cmd.options,
 			ServiceAccount: cmd.serviceAccountName,
 			LinkAccess:     cmd.linkAccessType,
 		},
@@ -218,7 +206,7 @@ func (cmd *CmdSiteCreate) WaitUntil() error {
 	})
 
 	if err != nil {
-		return fmt.Errorf("Site %q not configured yet, check the logs for more information\n", cmd.siteName)
+		return fmt.Errorf("Site %q not configured yet, check the status for more information\n", cmd.siteName)
 	}
 
 	fmt.Printf("Site %q is configured. Check the status to see when it is ready\n", cmd.siteName)
diff --git a/internal/cmd/skupper/site/kube/site_delete.go b/internal/cmd/skupper/site/kube/site_delete.go
index f43133e7..b795f454 100644
--- a/internal/cmd/skupper/site/kube/site_delete.go
+++ b/internal/cmd/skupper/site/kube/site_delete.go
@@ -113,7 +113,7 @@ func (cmd *CmdSiteDelete) WaitUntil() error {
 	})
 
 	if err != nil {
-		return fmt.Errorf("Site %q not deleted yet, check the logs for more information\n", cmd.siteName)
+		return fmt.Errorf("Site %q not deleted yet, check the status for more information\n", cmd.siteName)
 	}
 
 	fmt.Printf("Site %q is deleted\n", cmd.siteName)
diff --git a/internal/cmd/skupper/site/kube/site_update.go b/internal/cmd/skupper/site/kube/site_update.go
index 1bf99fe0..53b47d32 100644
--- a/internal/cmd/skupper/site/kube/site_update.go
+++ b/internal/cmd/skupper/site/kube/site_update.go
@@ -244,7 +244,7 @@ func (cmd *CmdSiteUpdate) WaitUntil() error {
 	})
 
 	if err != nil {
-		return fmt.Errorf("Site %q not ready yet, check the logs for more information\n", cmd.siteName)
+		return fmt.Errorf("Site %q not ready yet, check the status for more information\n", cmd.siteName)
 	}
 
 	fmt.Printf("Site %q is updated\n", cmd.siteName)
diff --git a/internal/cmd/skupper/token/kube/token_issue.go b/internal/cmd/skupper/token/kube/token_issue.go
index e98afe96..3d84c072 100644
--- a/internal/cmd/skupper/token/kube/token_issue.go
+++ b/internal/cmd/skupper/token/kube/token_issue.go
@@ -191,7 +191,7 @@ func (cmd *CmdTokenIssue) WaitUntil() error {
 	})
 
 	if err != nil {
-		return fmt.Errorf("Grant %q not ready yet, check the logs for more information\n", cmd.grantName)
+		return fmt.Errorf("Grant %q not ready yet, check the status for more information\n", cmd.grantName)
 	}
 
 	fmt.Printf("\nGrant %q is ready\n", cmd.grantName)
diff --git a/internal/cmd/skupper/token/kube/token_redeem.go b/internal/cmd/skupper/token/kube/token_redeem.go
index b85ebec2..46afecd1 100644
--- a/internal/cmd/skupper/token/kube/token_redeem.go
+++ b/internal/cmd/skupper/token/kube/token_redeem.go
@@ -170,7 +170,7 @@ func (cmd *CmdTokenRedeem) WaitUntil() error {
 	})
 
 	if err != nil {
-		return fmt.Errorf("Token %q redeem not ready yet, check the logs for more information\n", cmd.name)
+		return fmt.Errorf("Token %q redeem not ready yet, check the status for more information\n", cmd.name)
 	}
 
 	fmt.Printf("Token %q has been redeemed\n", cmd.name)
diff --git a/internal/cmd/skupper/utils/config.go b/internal/cmd/skupper/utils/config.go
new file mode 100644
index 00000000..bd7ff1e3
--- /dev/null
+++ b/internal/cmd/skupper/utils/config.go
@@ -0,0 +1,23 @@
+package utils
+
+import "time"
+
+type RetryProfile struct {
+	MinimumTimeout  time.Duration
+	MinimumInterval time.Duration
+}
+
+var DefaultRetryProfile = RetryProfile{
+	MinimumTimeout:  30 * time.Second,
+	MinimumInterval: 1 * time.Second,
+}
+
+var TestRetryProfile = RetryProfile{
+	MinimumTimeout:  1 * time.Second,
+	MinimumInterval: 1 * time.Millisecond,
+}
+
+var selectedProfile = DefaultRetryProfile
+
+func GetConfiguredRetryProfile() RetryProfile { return selectedProfile }
+func SetRetryProfile(profile RetryProfile)    { selectedProfile = profile }
diff --git a/internal/cmd/skupper/utils/encode.go b/internal/cmd/skupper/utils/encode.go
index fd0554cd..aaf70a85 100644
--- a/internal/cmd/skupper/utils/encode.go
+++ b/internal/cmd/skupper/utils/encode.go
@@ -3,6 +3,7 @@ package utils
 import (
 	"encoding/json"
 	"fmt"
+	"reflect"
 	"sigs.k8s.io/yaml"
 )
 
@@ -13,12 +14,75 @@ func Encode(outputType string, resource interface{}) (string, error) {
 
 	switch outputType {
 	case "json":
-		result, err = json.MarshalIndent(resource, "", "  ")
+		{
+			initialMap := make(map[string]interface{})
+			jsonData, err := json.MarshalIndent(resource, "", "  ")
+			if err != nil {
+				return "", err
+			}
+
+			err = json.Unmarshal(jsonData, &initialMap)
+			if err != nil {
+				return "", err
+			}
+
+			cleanedMap := omitEmptyValues(initialMap)
+			result, err = json.MarshalIndent(cleanedMap, "", "  ")
+		}
 	case "yaml":
-		result, err = yaml.Marshal(resource)
+		{
+			initialMap := make(map[string]interface{})
+			yamlData, err := yaml.Marshal(resource)
+			if err != nil {
+				return "", err
+			}
+
+			err = yaml.Unmarshal(yamlData, &initialMap)
+			if err != nil {
+				return "", err
+			}
+
+			cleanedMap := omitEmptyValues(initialMap)
+
+			result, err = yaml.Marshal(cleanedMap)
+		}
+
 	default:
 		return "", fmt.Errorf("format %s not supported", outputType)
 	}
 
 	return string(result), err
 }
+
+func omitEmptyValues(data map[string]interface{}) map[string]interface{} {
+	cleaned := make(map[string]interface{})
+	for k, v := range data {
+		if !isZeroValue(v) {
+			if reflect.TypeOf(v).Kind() == reflect.Map {
+				if vm, ok := v.(map[string]interface{}); ok {
+					v = omitEmptyValues(vm)
+				}
+			}
+			cleaned[k] = v
+		}
+	}
+	return cleaned
+}
+
+func isZeroValue(x interface{}) bool {
+	if x == nil {
+		return true
+	}
+
+	v := reflect.ValueOf(x)
+	switch v.Kind() {
+	case reflect.Map, reflect.Slice, reflect.Array:
+		return v.Len() == 0
+	case reflect.Struct:
+		z := reflect.New(v.Type()).Elem().Interface()
+		return reflect.DeepEqual(x, z)
+	default:
+		z := reflect.Zero(v.Type()).Interface()
+		return reflect.DeepEqual(x, z)
+	}
+}
diff --git a/internal/cmd/skupper/utils/handle_error.go b/internal/cmd/skupper/utils/handle_error.go
index 821c6e43..51623f21 100644
--- a/internal/cmd/skupper/utils/handle_error.go
+++ b/internal/cmd/skupper/utils/handle_error.go
@@ -5,10 +5,15 @@ import (
 	"syscall"
 )
 
+const (
+	GenericError    = 1
+	ValidationError = 2
+)
+
 func HandleError(err error) {
 	if err != nil {
 		fmt.Println(err)
-		syscall.Exit(0)
+		syscall.Exit(GenericError)
 	}
 }
 
@@ -18,7 +23,7 @@ func HandleErrorList(errList []error) {
 			fmt.Println(err)
 		}
 
-		syscall.Exit(0)
+		syscall.Exit(ValidationError)
 	}
 }
 
diff --git a/internal/cmd/skupper/utils/spinner.go b/internal/cmd/skupper/utils/spinner.go
index 13bafde0..526a248e 100644
--- a/internal/cmd/skupper/utils/spinner.go
+++ b/internal/cmd/skupper/utils/spinner.go
@@ -2,14 +2,15 @@ package utils
 
 import (
 	"context"
+	"time"
+
 	"github.com/briandowns/spinner"
 	"github.com/skupperproject/skupper/pkg/utils"
-	"time"
 )
 
 func NewSpinner(message string, maxRetries int, function func() error) error {
 
-	spin := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
+	spin := spinner.New(spinner.CharSets[9], 100*time.Millisecond, spinner.WithHiddenCursor(false))
 	spin.Prefix = message
 	spin.FinalMSG = message + "\n"
 
@@ -28,6 +29,7 @@ func NewSpinner(message string, maxRetries int, function func() error) error {
 
 func NewSpinnerWithTimeout(message string, timeoutInSeconds int, function func() error) error {
 
+	retryProfile := GetConfiguredRetryProfile()
 	spin := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
 	spin.Prefix = message
 	spin.FinalMSG = message + "\n"
@@ -37,7 +39,7 @@ func NewSpinnerWithTimeout(message string, timeoutInSeconds int, function func()
 
 	spin.Start()
 
-	err := utils.RetryErrorWithContext(ctx, time.Second, function)
+	err := utils.RetryErrorWithContext(ctx, retryProfile.MinimumInterval, function)
 
 	spin.Stop()
 
diff --git a/pkg/utils/spinner.go b/pkg/utils/spinner.go
deleted file mode 100644
index fb46eb4e..00000000
--- a/pkg/utils/spinner.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package utils
-
-import (
-	"github.com/briandowns/spinner"
-	"time"
-)
-
-func NewSpinner(message string, maxRetries int, function func() error) error {
-
-	spin := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
-	spin.Prefix = message
-	spin.FinalMSG = message + "\n"
-
-	spin.Start()
-
-	err := RetryError(time.Second, maxRetries, function)
-
-	spin.Stop()
-
-	if err != nil {
-		return err
-	}
-
-	return nil
-}


Test patch:
diff --git a/internal/cmd/skupper/connector/kube/connector_create_test.go b/internal/cmd/skupper/connector/kube/connector_create_test.go
index cc6c79fa..6e938567 100644
--- a/internal/cmd/skupper/connector/kube/connector_create_test.go
+++ b/internal/cmd/skupper/connector/kube/connector_create_test.go
@@ -934,6 +934,8 @@ func TestCmdConnectorCreate_WaitUntil(t *testing.T) {
 
 func newCmdConnectorCreateWithMocks(namespace string, k8sObjects []runtime.Object, skupperObjects []runtime.Object, fakeSkupperError string) (*CmdConnectorCreate, error) {
 
+	// We make sure the interval is appropriate
+	utils.SetRetryProfile(utils.TestRetryProfile)
 	client, err := fakeclient.NewFakeClient(namespace, k8sObjects, skupperObjects, fakeSkupperError)
 	if err != nil {
 		return nil, err
diff --git a/internal/cmd/skupper/connector/kube/connector_delete_test.go b/internal/cmd/skupper/connector/kube/connector_delete_test.go
index 5711d823..e2ae040d 100644
--- a/internal/cmd/skupper/connector/kube/connector_delete_test.go
+++ b/internal/cmd/skupper/connector/kube/connector_delete_test.go
@@ -246,6 +246,8 @@ func TestCmdConnectorDelete_WaitUntil(t *testing.T) {
 
 func newCmdConnectorDeleteWithMocks(namespace string, k8sObjects []runtime.Object, skupperObjects []runtime.Object, fakeSkupperError string) (*CmdConnectorDelete, error) {
 
+	// We make sure the interval is appropriate
+	utils.SetRetryProfile(utils.TestRetryProfile)
 	client, err := fakeclient.NewFakeClient(namespace, k8sObjects, skupperObjects, fakeSkupperError)
 	if err != nil {
 		return nil, err
diff --git a/internal/cmd/skupper/connector/kube/connector_update_test.go b/internal/cmd/skupper/connector/kube/connector_update_test.go
index 2e930d2f..ca2affd1 100644
--- a/internal/cmd/skupper/connector/kube/connector_update_test.go
+++ b/internal/cmd/skupper/connector/kube/connector_update_test.go
@@ -601,6 +601,8 @@ func TestCmdConnectorUpdate_WaitUntil(t *testing.T) {
 
 func newCmdConnectorUpdateWithMocks(namespace string, k8sObjects []runtime.Object, skupperObjects []runtime.Object, fakeSkupperError string) (*CmdConnectorUpdate, error) {
 
+	// We make sure the interval is appropriate
+	utils.SetRetryProfile(utils.TestRetryProfile)
 	client, err := fakeclient.NewFakeClient(namespace, k8sObjects, skupperObjects, fakeSkupperError)
 	if err != nil {
 		return nil, err
diff --git a/internal/cmd/skupper/link/kube/link_delete_test.go b/internal/cmd/skupper/link/kube/link_delete_test.go
index 84bfad6f..cf59a085 100644
--- a/internal/cmd/skupper/link/kube/link_delete_test.go
+++ b/internal/cmd/skupper/link/kube/link_delete_test.go
@@ -357,12 +357,12 @@ func TestCmdLinkDelete_WaitUntil(t *testing.T) {
 					},
 				},
 			},
-			timeout:     3,
+			timeout:     1,
 			expectError: true,
 		},
 		{
 			name:        "link is deleted",
-			timeout:     3,
+			timeout:     1,
 			expectError: false,
 		},
 	}
@@ -387,6 +387,8 @@ func TestCmdLinkDelete_WaitUntil(t *testing.T) {
 
 func newCmdLinkDeleteWithMocks(namespace string, k8sObjects []runtime.Object, skupperObjects []runtime.Object, fakeSkupperError string) (*CmdLinkDelete, error) {
 
+	// We make sure the interval is appropriate
+	utils.SetRetryProfile(utils.TestRetryProfile)
 	client, err := fakeclient.NewFakeClient(namespace, k8sObjects, skupperObjects, fakeSkupperError)
 	if err != nil {
 		return nil, err
diff --git a/internal/cmd/skupper/link/kube/link_generate_test.go b/internal/cmd/skupper/link/kube/link_generate_test.go
index b76ae04b..a4921dba 100644
--- a/internal/cmd/skupper/link/kube/link_generate_test.go
+++ b/internal/cmd/skupper/link/kube/link_generate_test.go
@@ -869,7 +869,7 @@ func TestCmdLinkGenerate_WaitUntil(t *testing.T) {
 		{
 			name:               "secret is not returned",
 			outputType:         "yaml",
-			timeout:            3,
+			timeout:            1,
 			tlsSecret:          "linkSecret",
 			generateCredential: true,
 			expectError:        true,
@@ -878,21 +878,21 @@ func TestCmdLinkGenerate_WaitUntil(t *testing.T) {
 			name:               "the output only contains the link",
 			generateCredential: false,
 			outputType:         "yaml",
-			timeout:            3,
+			timeout:            1,
 			expectError:        false,
 		},
 		{
 			name:               "bad format for the output",
 			generateCredential: false,
 			outputType:         "not supported",
-			timeout:            3,
+			timeout:            1,
 			expectError:        true,
 		},
 		{
 			name:               "the output contains the link and the secret",
 			generateCredential: true,
 			outputType:         "yaml",
-			timeout:            3,
+			timeout:            1,
 			tlsSecret:          "link-test",
 			k8sObjects: []runtime.Object{
 				&corev1.Secret{
@@ -926,7 +926,7 @@ func TestCmdLinkGenerate_WaitUntil(t *testing.T) {
 			name:               "the output contains the link and the secret, but it failed while deleting the certificate",
 			generateCredential: true,
 			outputType:         "yaml",
-			timeout:            3,
+			timeout:            1,
 			tlsSecret:          "link-test",
 			k8sObjects: []runtime.Object{
 				&corev1.Secret{
@@ -977,6 +977,9 @@ func TestCmdLinkGenerate_WaitUntil(t *testing.T) {
 
 func newCmdLinkGenerateWithMocks(namespace string, k8sObjects []runtime.Object, skupperObjects []runtime.Object, fakeSkupperError string) (*CmdLinkGenerate, error) {
 
+	// We make sure the interval is appropriate
+	utils.SetRetryProfile(utils.TestRetryProfile)
+
 	client, err := fakeclient.NewFakeClient(namespace, k8sObjects, skupperObjects, fakeSkupperError)
 	if err != nil {
 		return nil, err
diff --git a/internal/cmd/skupper/link/kube/link_update_test.go b/internal/cmd/skupper/link/kube/link_update_test.go
index 1689be24..26db1697 100644
--- a/internal/cmd/skupper/link/kube/link_update_test.go
+++ b/internal/cmd/skupper/link/kube/link_update_test.go
@@ -560,20 +560,20 @@ func TestCmdLinkUpdate_WaitUntil(t *testing.T) {
 				},
 			},
 			linkName:    "my-link",
-			timeout:     3,
+			timeout:     1,
 			expectError: true,
 		},
 		{
 			name:        "link is not returned",
 			linkName:    "my-link",
-			timeout:     3,
+			timeout:     1,
 			expectError: true,
 		},
 		{
 			name:        "there is no need to wait for a link, the user just wanted the output",
 			linkName:    "my-link",
 			output:      "json",
-			timeout:     3,
+			timeout:     1,
 			expectError: false,
 		},
 		{
@@ -601,7 +601,7 @@ func TestCmdLinkUpdate_WaitUntil(t *testing.T) {
 				},
 			},
 			linkName:    "my-link",
-			timeout:     3,
+			timeout:     1,
 			expectError: false,
 		},
 	}
@@ -631,6 +631,9 @@ func TestCmdLinkUpdate_WaitUntil(t *testing.T) {
 
 func newCmdLinkUpdateWithMocks(namespace string, k8sObjects []runtime.Object, skupperObjects []runtime.Object, fakeSkupperError string) (*CmdLinkUpdate, error) {
 
+	// We make sure the interval is appropriate
+	utils.SetRetryProfile(utils.TestRetryProfile)
+
 	client, err := fakeclient.NewFakeClient(namespace, k8sObjects, skupperObjects, fakeSkupperError)
 	if err != nil {
 		return nil, err
diff --git a/internal/cmd/skupper/listener/kube/listener_create_test.go b/internal/cmd/skupper/listener/kube/listener_create_test.go
index 6e611a82..62505108 100644
--- a/internal/cmd/skupper/listener/kube/listener_create_test.go
+++ b/internal/cmd/skupper/listener/kube/listener_create_test.go
@@ -808,6 +808,8 @@ func TestCmdListenerCreate_WaitUntil(t *testing.T) {
 
 func newCmdListenerCreateWithMocks(namespace string, k8sObjects []runtime.Object, skupperObjects []runtime.Object, fakeSkupperError string) (*CmdListenerCreate, error) {
 
+	// We make sure the interval is appropriate
+	utils.SetRetryProfile(utils.TestRetryProfile)
 	client, err := fakeclient.NewFakeClient(namespace, k8sObjects, skupperObjects, fakeSkupperError)
 	if err != nil {
 		return nil, err
diff --git a/internal/cmd/skupper/listener/kube/listener_delete_test.go b/internal/cmd/skupper/listener/kube/listener_delete_test.go
index 8450b731..70d0d9f4 100644
--- a/internal/cmd/skupper/listener/kube/listener_delete_test.go
+++ b/internal/cmd/skupper/listener/kube/listener_delete_test.go
@@ -269,6 +269,9 @@ func TestCmdListenerDelete_WaitUntil(t *testing.T) {
 
 func newCmdListenerDeleteWithMocks(namespace string, k8sObjects []runtime.Object, skupperObjects []runtime.Object, fakeSkupperError string) (*CmdListenerDelete, error) {
 
+	// We make sure the interval is appropriate
+	utils.SetRetryProfile(utils.TestRetryProfile)
+
 	client, err := fakeclient.NewFakeClient(namespace, k8sObjects, skupperObjects, fakeSkupperError)
 	if err != nil {
 		return nil, err
diff --git a/internal/cmd/skupper/listener/kube/listener_update_test.go b/internal/cmd/skupper/listener/kube/listener_update_test.go
index 7f0509fc..cb5b2019 100644
--- a/internal/cmd/skupper/listener/kube/listener_update_test.go
+++ b/internal/cmd/skupper/listener/kube/listener_update_test.go
@@ -533,6 +533,9 @@ func TestCmdListenerUpdate_WaitUntil(t *testing.T) {
 
 func newCmdListenerUpdateWithMocks(namespace string, k8sObjects []runtime.Object, skupperObjects []runtime.Object, fakeSkupperError string) (*CmdListenerUpdate, error) {
 
+	// We make sure the interval is appropriate
+	utils.SetRetryProfile(utils.TestRetryProfile)
+
 	client, err := fakeclient.NewFakeClient(namespace, k8sObjects, skupperObjects, fakeSkupperError)
 	if err != nil {
 		return nil, err
diff --git a/internal/cmd/skupper/site/kube/site_create_test.go b/internal/cmd/skupper/site/kube/site_create_test.go
index 9064b591..17d2a10f 100644
--- a/internal/cmd/skupper/site/kube/site_create_test.go
+++ b/internal/cmd/skupper/site/kube/site_create_test.go
@@ -168,60 +168,44 @@ func TestCmdSiteCreate_InputToOptions(t *testing.T) {
 		name               string
 		args               []string
 		flags              CreateFlags
-		expectedSettings   map[string]string
 		expectedLinkAccess string
 		expectedOutput     string
 	}
 
 	testTable := []test{
 		{
-			name:  "options without link access enabled",
-			args:  []string{"my-site"},
-			flags: CreateFlags{},
-			expectedSettings: map[string]string{
-				"name": "my-site",
-			},
-			expectedLinkAccess: "none",
+			name:               "options without link access enabled",
+			args:               []string{"my-site"},
+			flags:              CreateFlags{},
+			expectedLinkAccess: "",
 			expectedOutput:     "",
 		},
 		{
-			name:  "options with link access enabled but using a type by default and link access host specified",
-			args:  []string{"my-site"},
-			flags: CreateFlags{enableLinkAccess: true},
-			expectedSettings: map[string]string{
-				"name": "my-site",
-			},
+			name:               "options with link access enabled but using a type by default",
+			args:               []string{"my-site"},
+			flags:              CreateFlags{enableLinkAccess: true, linkAccessType: "loadbalancer"},
 			expectedLinkAccess: "loadbalancer",
 			expectedOutput:     "",
 		},
 		{
-			name:  "options with link access enabled using the nodeport type",
-			args:  []string{"my-site"},
-			flags: CreateFlags{enableLinkAccess: true, linkAccessType: "nodeport"},
-			expectedSettings: map[string]string{
-				"name": "my-site",
-			},
+			name:               "options with link access enabled using the nodeport type",
+			args:               []string{"my-site"},
+			flags:              CreateFlags{enableLinkAccess: true, linkAccessType: "nodeport"},
 			expectedLinkAccess: "nodeport",
 			expectedOutput:     "",
 		},
 		{
-			name:  "options with link access options not well specified",
-			args:  []string{"my-site"},
-			flags: CreateFlags{enableLinkAccess: false, linkAccessType: "nodeport"},
-			expectedSettings: map[string]string{
-				"name": "my-site",
-			},
-			expectedLinkAccess: "none",
+			name:               "options with link access options not well specified",
+			args:               []string{"my-site"},
+			flags:              CreateFlags{enableLinkAccess: false, linkAccessType: "nodeport"},
+			expectedLinkAccess: "",
 			expectedOutput:     "",
 		},
 		{
-			name:  "options output type",
-			args:  []string{"my-site"},
-			flags: CreateFlags{enableLinkAccess: false, linkAccessType: "nodeport", output: "yaml"},
-			expectedSettings: map[string]string{
-				"name": "my-site",
-			},
-			expectedLinkAccess: "none",
+			name:               "options output type",
+			args:               []string{"my-site"},
+			flags:              CreateFlags{enableLinkAccess: false, linkAccessType: "nodeport", output: "yaml"},
+			expectedLinkAccess: "",
 			expectedOutput:     "yaml",
 		},
 	}
@@ -235,9 +219,8 @@ func TestCmdSiteCreate_InputToOptions(t *testing.T) {
 
 			cmd.InputToOptions()
 
-			assert.DeepEqual(t, cmd.options, test.expectedSettings)
-
 			assert.Check(t, cmd.output == test.expectedOutput)
+			assert.Check(t, cmd.linkAccessType == test.expectedLinkAccess)
 		})
 	}
 }
@@ -321,7 +304,6 @@ func TestCmdSiteCreate_Run(t *testing.T) {
 
 		command.siteName = test.siteName
 		command.serviceAccountName = test.serviceAccountName
-		command.options = test.options
 		command.output = test.output
 
 		t.Run(test.name, func(t *testing.T) {
diff --git a/internal/cmd/skupper/site/kube/site_update_test.go b/internal/cmd/skupper/site/kube/site_update_test.go
index 68ba1dd3..c15e2923 100644
--- a/internal/cmd/skupper/site/kube/site_update_test.go
+++ b/internal/cmd/skupper/site/kube/site_update_test.go
@@ -590,7 +590,7 @@ func TestCmdSiteUpdate_WaitUntil(t *testing.T) {
 			},
 			siteName:     "my-site",
 			skupperError: "",
-			errorMessage: "Site \"my-site\" not ready yet, check the logs for more information\n",
+			errorMessage: "Site \"my-site\" not ready yet, check the status for more information\n",
 			expectError:  true,
 		},
 	}
diff --git a/internal/cmd/skupper/token/kube/token_issue_test.go b/internal/cmd/skupper/token/kube/token_issue_test.go
index f69b69b0..e243e7f4 100644
--- a/internal/cmd/skupper/token/kube/token_issue_test.go
+++ b/internal/cmd/skupper/token/kube/token_issue_test.go
@@ -739,6 +739,9 @@ func TestCmdTokenIssue_WaitUntil(t *testing.T) {
 
 func newCmdTokenIssueWithMocks(namespace string, k8sObjects []runtime.Object, skupperObjects []runtime.Object, fakeSkupperError string) (*CmdTokenIssue, error) {
 
+	// We make sure the interval is appropriate
+	utils.SetRetryProfile(utils.TestRetryProfile)
+
 	client, err := fakeclient.NewFakeClient(namespace, k8sObjects, skupperObjects, fakeSkupperError)
 	if err != nil {
 		return nil, err
diff --git a/internal/cmd/skupper/token/kube/token_redeem_test.go b/internal/cmd/skupper/token/kube/token_redeem_test.go
index 1f1d9e31..94d5863e 100644
--- a/internal/cmd/skupper/token/kube/token_redeem_test.go
+++ b/internal/cmd/skupper/token/kube/token_redeem_test.go
@@ -471,6 +471,8 @@ func newCmdCreateAccessGrantFile(fileName string) error {
 
 func newCmdTokenRedeemWithMocks(namespace string, k8sObjects []runtime.Object, skupperObjects []runtime.Object, fakeSkupperError string) (*CmdTokenRedeem, error) {
 
+	// We make sure the interval is appropriate
+	utils.SetRetryProfile(utils.TestRetryProfile)
 	client, err := fakeclient.NewFakeClient(namespace, k8sObjects, skupperObjects, fakeSkupperError)
 	if err != nil {
 		return nil, err
diff --git a/internal/cmd/skupper/utils/encode_test.go b/internal/cmd/skupper/utils/encode_test.go
index 0d6291a3..e85ee290 100644
--- a/internal/cmd/skupper/utils/encode_test.go
+++ b/internal/cmd/skupper/utils/encode_test.go
@@ -27,17 +27,15 @@ func Test_Marshal(t *testing.T) {
 			},
 		},
 			`{
-  "kind": "Site",
   "apiVersion": "skupper.io/v1alpha1",
+  "kind": "Site",
   "metadata": {
     "name": "my-site",
-    "namespace": "test",
-    "creationTimestamp": null
+    "namespace": "test"
   },
   "spec": {
     "linkAccess": "default"
-  },
-  "status": {}
+  }
 }`, false},
 		{"yaml", v1alpha1.Site{
 			TypeMeta: v1.TypeMeta{
@@ -55,12 +53,10 @@ func Test_Marshal(t *testing.T) {
 			`apiVersion: skupper.io/v1alpha1
 kind: Site
 metadata:
-  creationTimestamp: null
   name: my-site
   namespace: test
 spec:
   linkAccess: default
-status: {}
 `,
 			false},
 		{"unsupported", v1alpha1.Site{