Repository: skupperproject/skupper
Pull request #1655 (2024-09-13 19:10:29)

Unify timeout flag accross all applicable commands
- All site and link commands (create, update and delete) have now a timeout flag 
- Unification of the timeout flag to use time.Duration instead of string values


Patch:
diff --git a/internal/cmd/skupper/common/flags.go b/internal/cmd/skupper/common/flags.go
index 53d529e7..fa3ed858 100644
--- a/internal/cmd/skupper/common/flags.go
+++ b/internal/cmd/skupper/common/flags.go
@@ -75,6 +75,8 @@ type CommandSiteCreateFlags struct {
 	LinkAccessType          string
 	ServiceAccount          string
 	Output                  string
+	Host                    string
+	Timeout                 time.Duration
 	BindHost                string
 	SubjectAlternativeNames []string
 }
@@ -84,26 +86,32 @@ type CommandSiteUpdateFlags struct {
 	LinkAccessType          string
 	ServiceAccount          string
 	Output                  string
+	Host                    string
+	Timeout                 time.Duration
 	BindHost                string
 	SubjectAlternativeNames []string
 }
 
+type CommandSiteDeleteFlags struct {
+	Timeout time.Duration
+}
+
 type CommandLinkGenerateFlags struct {
 	TlsSecret          string
 	Cost               string
 	Output             string
 	GenerateCredential bool
-	Timeout            string
+	Timeout            time.Duration
 }
 type CommandLinkUpdateFlags struct {
 	TlsSecret string
 	Cost      string
 	Output    string
-	Timeout   string
+	Timeout   time.Duration
 }
 
 type CommandLinkDeleteFlags struct {
-	Timeout string
+	Timeout time.Duration
 }
 type CommandLinkStatusFlags struct {
 	Output string
diff --git a/internal/cmd/skupper/link/kube/link_delete.go b/internal/cmd/skupper/link/kube/link_delete.go
index 00090d77..deb1192c 100644
--- a/internal/cmd/skupper/link/kube/link_delete.go
+++ b/internal/cmd/skupper/link/kube/link_delete.go
@@ -10,7 +10,7 @@ import (
 	"github.com/skupperproject/skupper/pkg/utils/validator"
 	"github.com/spf13/cobra"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"strconv"
+	"time"
 )
 
 type CmdLinkDelete struct {
@@ -19,7 +19,7 @@ type CmdLinkDelete struct {
 	Namespace string
 	Flags     *common.CommandLinkDeleteFlags
 	linkName  string
-	timeout   int
+	timeout   time.Duration
 }
 
 func NewCmdLinkDelete() *CmdLinkDelete {
@@ -56,20 +56,16 @@ func (cmd *CmdLinkDelete) ValidateInput(args []string) []error {
 			validationErrors = append(validationErrors, fmt.Errorf("the link %q is not available in the namespace", cmd.linkName))
 		}
 	}
-	selectedTimeout, conversionErr := strconv.Atoi(cmd.Flags.Timeout)
-	if conversionErr != nil {
-		validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid: %s", conversionErr))
-	} else {
-		ok, err := timeoutValidator.Evaluate(selectedTimeout)
-		if !ok {
-			validationErrors = append(validationErrors, fmt.Errorf("timeout 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))
 	}
 
 	return validationErrors
 }
 func (cmd *CmdLinkDelete) InputToOptions() {
-	cmd.timeout, _ = strconv.Atoi(cmd.Flags.Timeout)
+	cmd.timeout = cmd.Flags.Timeout
 }
 
 func (cmd *CmdLinkDelete) Run() error {
@@ -77,7 +73,8 @@ func (cmd *CmdLinkDelete) Run() error {
 	return err
 }
 func (cmd *CmdLinkDelete) WaitUntil() error {
-	err := utils.NewSpinnerWithTimeout("Waiting for deletion to complete...", cmd.timeout, func() error {
+	waitTime := int(cmd.timeout.Seconds())
+	err := utils.NewSpinnerWithTimeout("Waiting for deletion to complete...", waitTime, func() error {
 
 		resource, err := cmd.Client.Links(cmd.Namespace).Get(context.TODO(), cmd.linkName, metav1.GetOptions{})
 
diff --git a/internal/cmd/skupper/link/kube/link_generate.go b/internal/cmd/skupper/link/kube/link_generate.go
index ad712b1f..3ed88866 100644
--- a/internal/cmd/skupper/link/kube/link_generate.go
+++ b/internal/cmd/skupper/link/kube/link_generate.go
@@ -35,7 +35,7 @@ type CmdLinkGenerate struct {
 	activeSite         *v1alpha1.Site
 	generateCredential bool
 	generatedLink      v1alpha1.Link
-	timeout            int
+	timeout            time.Duration
 }
 
 func NewCmdLinkGenerate() *CmdLinkGenerate {
@@ -107,14 +107,9 @@ func (cmd *CmdLinkGenerate) ValidateInput(args []string) []error {
 		}
 	}
 
-	selectedTimeout, convErr := strconv.Atoi(cmd.Flags.Timeout)
-	if convErr != nil {
-		validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid: %s", convErr))
-	} else {
-		ok, err = timeoutValidator.Evaluate(selectedTimeout)
-		if !ok {
-			validationErrors = append(validationErrors, fmt.Errorf("timeout 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))
 	}
 
 	return validationErrors
@@ -125,7 +120,7 @@ func (cmd *CmdLinkGenerate) InputToOptions() {
 	cmd.cost, _ = strconv.Atoi(cmd.Flags.Cost)
 	cmd.output = cmd.Flags.Output
 	cmd.generateCredential = cmd.Flags.GenerateCredential
-	cmd.timeout, _ = strconv.Atoi(cmd.Flags.Timeout)
+	cmd.timeout = cmd.Flags.Timeout
 
 	var generatedLinkName string
 	if cmd.activeSite != nil {
@@ -207,7 +202,7 @@ func (cmd *CmdLinkGenerate) WaitUntil() error {
 
 	if cmd.generateCredential {
 
-		ctxWithTimeout, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(cmd.timeout))
+		ctxWithTimeout, cancel := context.WithTimeout(context.Background(), cmd.timeout)
 		defer cancel()
 
 		err := pkgutils.RetryErrorWithContext(ctxWithTimeout, time.Second, func() error {
diff --git a/internal/cmd/skupper/link/kube/link_update.go b/internal/cmd/skupper/link/kube/link_update.go
index 2495b58d..bb9f40e6 100644
--- a/internal/cmd/skupper/link/kube/link_update.go
+++ b/internal/cmd/skupper/link/kube/link_update.go
@@ -16,6 +16,7 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/kubernetes"
 	"strconv"
+	"time"
 )
 
 type CmdLinkUpdate struct {
@@ -28,7 +29,7 @@ type CmdLinkUpdate struct {
 	tlsSecret  string
 	cost       int
 	output     string
-	timeout    int
+	timeout    time.Duration
 }
 
 func NewCmdLinkUpdate() *CmdLinkUpdate {
@@ -92,14 +93,9 @@ func (cmd *CmdLinkUpdate) ValidateInput(args []string) []error {
 		}
 	}
 
-	selectedTimeout, convErr := strconv.Atoi(cmd.Flags.Timeout)
-	if convErr != nil {
-		validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid: %s", convErr))
-	} else {
-		ok, err = timeoutValidator.Evaluate(selectedTimeout)
-		if !ok {
-			validationErrors = append(validationErrors, fmt.Errorf("timeout 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))
 	}
 
 	return validationErrors
@@ -110,7 +106,7 @@ func (cmd *CmdLinkUpdate) InputToOptions() {
 	cmd.cost, _ = strconv.Atoi(cmd.Flags.Cost)
 	cmd.tlsSecret = cmd.Flags.TlsSecret
 	cmd.output = cmd.Flags.Output
-	cmd.timeout, _ = strconv.Atoi(cmd.Flags.Timeout)
+	cmd.timeout = cmd.Flags.Timeout
 
 }
 
@@ -169,7 +165,8 @@ func (cmd *CmdLinkUpdate) WaitUntil() error {
 		return nil
 	}
 
-	err := utils.NewSpinnerWithTimeout("Waiting for update to complete...", cmd.timeout, func() error {
+	waitTime := int(cmd.timeout.Seconds())
+	err := utils.NewSpinnerWithTimeout("Waiting for update to complete...", waitTime, func() error {
 
 		resource, err := cmd.Client.Links(cmd.Namespace).Get(context.TODO(), cmd.linkName, metav1.GetOptions{})
 		if err != nil {
diff --git a/internal/cmd/skupper/link/link.go b/internal/cmd/skupper/link/link.go
index d5f54dad..e6e6246a 100644
--- a/internal/cmd/skupper/link/link.go
+++ b/internal/cmd/skupper/link/link.go
@@ -7,6 +7,7 @@ import (
 	"github.com/skupperproject/skupper/internal/cmd/skupper/link/nonkube"
 	"github.com/skupperproject/skupper/pkg/config"
 	"github.com/spf13/cobra"
+	"time"
 )
 
 func NewCmdLink() *cobra.Command {
@@ -44,7 +45,7 @@ output needs to be applied in the site in which we want to create the link.`,
 	cmd.Flags().StringVar(&cmdFlags.Cost, common.FlagNameCost, "1", common.FlagDescCost)
 	cmd.Flags().StringVarP(&cmdFlags.Output, common.FlagNameOutput, "o", "yaml", common.FlagDescOutput)
 	cmd.Flags().BoolVar(&cmdFlags.GenerateCredential, common.FlagNameGenerateCredential, true, common.FlagDescGenerateCredential)
-	cmd.Flags().StringVar(&cmdFlags.Timeout, common.FlagNameTimeout, "60", common.FlagDescTimeout)
+	cmd.Flags().DurationVar(&cmdFlags.Timeout, common.FlagNameTimeout, 60*time.Second, common.FlagDescTimeout)
 
 	kubeCommand.CobraCmd = cmd
 	kubeCommand.Flags = &cmdFlags
@@ -68,8 +69,8 @@ func CmdLinkUpdateFactory(configuredPlatform types.Platform) *cobra.Command {
 	cmdFlags := common.CommandLinkUpdateFlags{}
 	cmd.Flags().StringVar(&cmdFlags.TlsSecret, common.FlagNameTlsSecret, "", common.FlagDescTlsSecret)
 	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)
-	cmd.Flags().StringVar(&cmdFlags.Timeout, common.FlagNameTimeout, "60", common.FlagDescCost)
 
 	kubeCommand.CobraCmd = cmd
 	kubeCommand.Flags = &cmdFlags
@@ -115,7 +116,7 @@ func CmdLinkDeleteFactory(configuredPlatform types.Platform) *cobra.Command {
 
 	cmd := common.ConfigureCobraCommand(configuredPlatform, cmdLinkDeleteDesc, kubeCommand, nonKubeCommand)
 	cmdFlags := common.CommandLinkDeleteFlags{}
-	cmd.Flags().StringVar(&cmdFlags.Timeout, common.FlagNameTimeout, "60", common.FlagDescTimeout)
+	cmd.Flags().DurationVar(&cmdFlags.Timeout, common.FlagNameTimeout, 60*time.Second, common.FlagDescTimeout)
 
 	kubeCommand.CobraCmd = cmd
 	kubeCommand.Flags = &cmdFlags
diff --git a/internal/cmd/skupper/site/kube/site_create.go b/internal/cmd/skupper/site/kube/site_create.go
index 6d168ff2..9c5c41c7 100644
--- a/internal/cmd/skupper/site/kube/site_create.go
+++ b/internal/cmd/skupper/site/kube/site_create.go
@@ -15,6 +15,7 @@ import (
 	"github.com/spf13/cobra"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/kubernetes"
+	"time"
 )
 
 type CmdSiteCreate struct {
@@ -27,6 +28,7 @@ type CmdSiteCreate struct {
 	Namespace          string
 	linkAccessType     string
 	output             string
+	timeout            time.Duration
 }
 
 func NewCmdSiteCreate() *CmdSiteCreate {
@@ -51,6 +53,7 @@ func (cmd *CmdSiteCreate) ValidateInput(args []string) []error {
 	resourceStringValidator := validator.NewResourceStringValidator()
 	linkAccessTypeValidator := validator.NewOptionValidator(common.LinkAccessTypes)
 	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
+	timeoutValidator := validator.NewTimeoutInSecondsValidator()
 
 	//Validate if there is already a site defined in the namespace
 	siteList, _ := cmd.Client.Sites(cmd.Namespace).List(context.TODO(), metav1.ListOptions{})
@@ -104,6 +107,13 @@ func (cmd *CmdSiteCreate) ValidateInput(args []string) []error {
 		}
 	}
 
+	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))
+		}
+	}
+
 	return validationErrors
 }
 
@@ -120,6 +130,7 @@ func (cmd *CmdSiteCreate) InputToOptions() {
 	}
 
 	cmd.output = cmd.Flags.Output
+	cmd.timeout = cmd.Flags.Timeout
 
 }
 
@@ -160,7 +171,8 @@ func (cmd *CmdSiteCreate) WaitUntil() error {
 		return nil
 	}
 
-	err := utils.NewSpinner("Waiting for status...", 5, func() error {
+	waitTime := int(cmd.timeout.Seconds())
+	err := utils.NewSpinnerWithTimeout("Waiting for status...", waitTime, func() error {
 
 		resource, err := cmd.Client.Sites(cmd.Namespace).Get(context.TODO(), cmd.siteName, metav1.GetOptions{})
 		if err != nil {
diff --git a/internal/cmd/skupper/site/kube/site_delete.go b/internal/cmd/skupper/site/kube/site_delete.go
index 3b8eabe3..3a4d36f4 100644
--- a/internal/cmd/skupper/site/kube/site_delete.go
+++ b/internal/cmd/skupper/site/kube/site_delete.go
@@ -3,7 +3,10 @@ package kube
 import (
 	"context"
 	"fmt"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
+	"github.com/skupperproject/skupper/pkg/utils/validator"
+	"time"
 
 	"github.com/skupperproject/skupper/internal/kube/client"
 	skupperv1alpha1 "github.com/skupperproject/skupper/pkg/generated/client/clientset/versioned/typed/skupper/v1alpha1"
@@ -14,8 +17,10 @@ import (
 type CmdSiteDelete struct {
 	Client    skupperv1alpha1.SkupperV1alpha1Interface
 	CobraCmd  *cobra.Command
+	Flags     *common.CommandSiteDeleteFlags
 	Namespace string
 	siteName  string
+	timeout   time.Duration
 }
 
 func NewCmdSiteDelete() *CmdSiteDelete {
@@ -35,6 +40,7 @@ func (cmd *CmdSiteDelete) NewClient(cobraCommand *cobra.Command, args []string)
 
 func (cmd *CmdSiteDelete) ValidateInput(args []string) []error {
 	var validationErrors []error
+	timeoutValidator := validator.NewTimeoutInSecondsValidator()
 
 	//Validate if there is already a site defined in the namespace
 	siteList, err := cmd.Client.Sites(cmd.Namespace).List(context.TODO(), metav1.ListOptions{})
@@ -68,16 +74,26 @@ func (cmd *CmdSiteDelete) ValidateInput(args []string) []error {
 		}
 	}
 
+	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))
+		}
+	}
+
 	return validationErrors
 }
-func (cmd *CmdSiteDelete) InputToOptions() {}
+func (cmd *CmdSiteDelete) InputToOptions() {
+	cmd.timeout = cmd.Flags.Timeout
+}
 
 func (cmd *CmdSiteDelete) Run() error {
 	err := cmd.Client.Sites(cmd.Namespace).Delete(context.TODO(), cmd.siteName, metav1.DeleteOptions{})
 	return err
 }
 func (cmd *CmdSiteDelete) WaitUntil() error {
-	err := utils.NewSpinner("Waiting for deletion to complete...", 5, func() error {
+	waitTime := int(cmd.timeout.Seconds())
+	err := utils.NewSpinnerWithTimeout("Waiting for deletion to complete...", waitTime, func() error {
 
 		resource, err := cmd.Client.Sites(cmd.Namespace).Get(context.TODO(), cmd.siteName, metav1.GetOptions{})
 
diff --git a/internal/cmd/skupper/site/kube/site_update.go b/internal/cmd/skupper/site/kube/site_update.go
index 62991532..dfda0343 100644
--- a/internal/cmd/skupper/site/kube/site_update.go
+++ b/internal/cmd/skupper/site/kube/site_update.go
@@ -6,11 +6,11 @@ import (
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"k8s.io/client-go/kubernetes"
+	"time"
 
 	"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"
@@ -21,12 +21,12 @@ type CmdSiteUpdate struct {
 	KubeClient         kubernetes.Interface
 	CobraCmd           *cobra.Command
 	Flags              *common.CommandSiteUpdateFlags
-	options            map[string]string
 	siteName           string
 	serviceAccountName string
 	Namespace          string
 	linkAccessType     string
 	output             string
+	timeout            time.Duration
 }
 
 func NewCmdSiteUpdate() *CmdSiteUpdate {
@@ -50,6 +50,7 @@ func (cmd *CmdSiteUpdate) ValidateInput(args []string) []error {
 	var validationErrors []error
 	linkAccessTypeValidator := validator.NewOptionValidator(common.LinkAccessTypes)
 	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
+	timeoutValidator := validator.NewTimeoutInSecondsValidator()
 
 	//Validate if there is already a site defined in the namespace
 	siteList, _ := cmd.Client.Sites(cmd.Namespace).List(context.TODO(), metav1.ListOptions{})
@@ -109,6 +110,13 @@ func (cmd *CmdSiteUpdate) ValidateInput(args []string) []error {
 		}
 	}
 
+	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))
+		}
+	}
+
 	return validationErrors
 }
 func (cmd *CmdSiteUpdate) InputToOptions() {
@@ -120,18 +128,10 @@ func (cmd *CmdSiteUpdate) InputToOptions() {
 		} else {
 			cmd.linkAccessType = cmd.Flags.LinkAccessType
 		}
-	} else {
-		cmd.linkAccessType = "none"
 	}
 
-	options := make(map[string]string)
-	if cmd.siteName != "" {
-		options[site.SiteConfigNameKey] = cmd.siteName
-	}
-
-	cmd.options = options
-
 	cmd.output = cmd.Flags.Output
+	cmd.timeout = cmd.Flags.Timeout
 
 }
 func (cmd *CmdSiteUpdate) Run() error {
@@ -143,9 +143,6 @@ func (cmd *CmdSiteUpdate) Run() error {
 	}
 
 	updatedSettings := currentSite.Spec.Settings
-	if len(cmd.options) != 0 {
-		updatedSettings = cmd.options
-	}
 
 	updatedServiceAccount := currentSite.Spec.ServiceAccount
 	if cmd.serviceAccountName != "" {
@@ -195,7 +192,8 @@ func (cmd *CmdSiteUpdate) WaitUntil() error {
 		return nil
 	}
 
-	err := utils.NewSpinner("Waiting for update to complete...", 5, func() error {
+	waitTime := int(cmd.timeout.Seconds())
+	err := utils.NewSpinnerWithTimeout("Waiting for update to complete...", waitTime, func() error {
 
 		resource, err := cmd.Client.Sites(cmd.Namespace).Get(context.TODO(), cmd.siteName, metav1.GetOptions{})
 		if err != nil {
diff --git a/internal/cmd/skupper/site/nonkube/site_delete.go b/internal/cmd/skupper/site/nonkube/site_delete.go
index 91642a0f..b174f9d4 100644
--- a/internal/cmd/skupper/site/nonkube/site_delete.go
+++ b/internal/cmd/skupper/site/nonkube/site_delete.go
@@ -2,6 +2,7 @@ package nonkube
 
 import (
 	"fmt"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 
 	"github.com/spf13/cobra"
 )
@@ -10,6 +11,7 @@ type CmdSiteDelete struct {
 	CobraCmd  *cobra.Command
 	Namespace string
 	siteName  string
+	Flags     *common.CommandSiteDeleteFlags
 }
 
 func NewCmdSiteDelete() *CmdSiteDelete {
diff --git a/internal/cmd/skupper/site/site.go b/internal/cmd/skupper/site/site.go
index 20433581..56dcd3d5 100644
--- a/internal/cmd/skupper/site/site.go
+++ b/internal/cmd/skupper/site/site.go
@@ -10,6 +10,7 @@ import (
 	"github.com/skupperproject/skupper/internal/cmd/skupper/site/nonkube"
 	"github.com/skupperproject/skupper/pkg/config"
 	"github.com/spf13/cobra"
+	"time"
 )
 
 func NewCmdSite() *cobra.Command {
@@ -50,6 +51,7 @@ There can be only one site definition per namespace.`,
 	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, 60*time.Second, common.FlagDescTimeout)
 	cmd.Flags().StringVar(&cmdFlags.BindHost, common.FlagNameBindHost, "", common.FlagDescBindHost)
 	cmd.Flags().StringSliceVar(&cmdFlags.SubjectAlternativeNames, common.FlagNameSubjectAlternativeNames, []string{}, common.FlagDescSubjectAlternativeNames)
 
@@ -87,6 +89,7 @@ func CmdSiteUpdateFactory(configuredPlatform types.Platform) *cobra.Command {
 	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, 60*time.Second, common.FlagDescTimeout)
 	cmd.Flags().StringVar(&cmdFlags.BindHost, common.FlagNameBindHost, "", common.FlagDescBindHost)
 	cmd.Flags().StringSliceVar(&cmdFlags.SubjectAlternativeNames, common.FlagNameSubjectAlternativeNames, []string{}, common.FlagDescSubjectAlternativeNames)
 
@@ -136,9 +139,14 @@ func CmdSiteDeleteFactory(configuredPlatform types.Platform) *cobra.Command {
 	}
 
 	cmd := common.ConfigureCobraCommand(configuredPlatform, cmdSiteDeleteDesc, kubeCommand, nonKubeCommand)
+	cmdFlags := common.CommandSiteDeleteFlags{}
+
+	cmd.Flags().DurationVar(&cmdFlags.Timeout, common.FlagNameTimeout, 60*time.Second, common.FlagDescTimeout)
 
 	kubeCommand.CobraCmd = cmd
+	kubeCommand.Flags = &cmdFlags
 	nonKubeCommand.CobraCmd = cmd
+	nonKubeCommand.Flags = &cmdFlags
 
 	return cmd
 }
diff --git a/pkg/utils/validator/simple_validator.go b/pkg/utils/validator/simple_validator.go
index ccc2a279..88630d1d 100644
--- a/pkg/utils/validator/simple_validator.go
+++ b/pkg/utils/validator/simple_validator.go
@@ -3,6 +3,7 @@ package validator
 import (
 	"fmt"
 	"regexp"
+	"time"
 )
 
 type Validator interface {
@@ -88,13 +89,6 @@ func NewNumberValidator() *NumberValidator {
 	}
 }
 
-func NewTimeoutInSecondsValidator() *NumberValidator {
-	return &NumberValidator{
-		PositiveInt: true,
-		IncludeZero: false,
-	}
-}
-
 func (i NumberValidator) Evaluate(value interface{}) (bool, error) {
 
 	v, ok := value.(int)
@@ -156,3 +150,22 @@ func (i OptionValidator) Evaluate(value interface{}) (bool, error) {
 	}
 	return true, nil
 }
+
+type DurationValidator struct {
+	MinDuration time.Duration
+}
+
+func NewTimeoutInSecondsValidator() *DurationValidator {
+	return &DurationValidator{
+		MinDuration: time.Second * 10,
+	}
+}
+
+func (i DurationValidator) Evaluate(value time.Duration) (bool, error) {
+
+	if value < i.MinDuration {
+		return false, fmt.Errorf("duration must not be less than %v; got %v", i.MinDuration, value)
+	}
+
+	return true, nil
+}


Test patch:
diff --git a/internal/cmd/skupper/link/kube/link_delete_test.go b/internal/cmd/skupper/link/kube/link_delete_test.go
index a32b379c..6bd4251f 100644
--- a/internal/cmd/skupper/link/kube/link_delete_test.go
+++ b/internal/cmd/skupper/link/kube/link_delete_test.go
@@ -9,6 +9,7 @@ import (
 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"testing"
+	"time"
 )
 
 func TestCmdLinkDelete_ValidateInput(t *testing.T) {
@@ -25,13 +26,13 @@ func TestCmdLinkDelete_ValidateInput(t *testing.T) {
 		{
 			name:           "there is no active skupper site in this namespace",
 			args:           []string{"my-link"},
-			flags:          common.CommandLinkDeleteFlags{Timeout: "60"},
+			flags:          common.CommandLinkDeleteFlags{Timeout: time.Minute},
 			expectedErrors: []string{"there is no skupper site in this namespace", "the link \"my-link\" is not available in the namespace"},
 		},
 		{
 			name:  "link is not deleted because it does not exist",
 			args:  []string{"my-link"},
-			flags: common.CommandLinkDeleteFlags{Timeout: "60"},
+			flags: common.CommandLinkDeleteFlags{Timeout: time.Minute},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.SiteList{
 					Items: []v1alpha1.Site{
@@ -49,7 +50,7 @@ func TestCmdLinkDelete_ValidateInput(t *testing.T) {
 		{
 			name:  "more than one argument was specified",
 			args:  []string{"my", "link"},
-			flags: common.CommandLinkDeleteFlags{Timeout: "60"},
+			flags: common.CommandLinkDeleteFlags{Timeout: time.Minute},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.SiteList{
 					Items: []v1alpha1.Site{
@@ -67,7 +68,7 @@ func TestCmdLinkDelete_ValidateInput(t *testing.T) {
 		{
 			name:  "trying to delete without specifying a name",
 			args:  []string{""},
-			flags: common.CommandLinkDeleteFlags{Timeout: "60"},
+			flags: common.CommandLinkDeleteFlags{Timeout: time.Minute},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.SiteList{
 					Items: []v1alpha1.Site{
@@ -85,7 +86,7 @@ func TestCmdLinkDelete_ValidateInput(t *testing.T) {
 		{
 			name:  "link deleted successfully",
 			args:  []string{"my-link"},
-			flags: common.CommandLinkDeleteFlags{Timeout: "60"},
+			flags: common.CommandLinkDeleteFlags{Timeout: time.Minute},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.SiteList{
 					Items: []v1alpha1.Site{
@@ -106,58 +107,10 @@ func TestCmdLinkDelete_ValidateInput(t *testing.T) {
 			},
 			expectedErrors: []string{},
 		},
-		{
-			name:  "timeout is not valid because it is negative",
-			args:  []string{"my-link"},
-			flags: common.CommandLinkDeleteFlags{Timeout: "-1"},
-			skupperObjects: []runtime.Object{
-				&v1alpha1.SiteList{
-					Items: []v1alpha1.Site{
-						{
-							ObjectMeta: v1.ObjectMeta{
-								Name:      "the-site",
-								Namespace: "test",
-							},
-						},
-					},
-				},
-				&v1alpha1.Link{
-					ObjectMeta: v1.ObjectMeta{
-						Name:      "my-link",
-						Namespace: "test",
-					},
-				},
-			},
-			expectedErrors: []string{"timeout is not valid: value is not positive"},
-		},
 		{
 			name:  "timeout is not valid because it is zero",
 			args:  []string{"my-link"},
-			flags: common.CommandLinkDeleteFlags{Timeout: "0"},
-			skupperObjects: []runtime.Object{
-				&v1alpha1.SiteList{
-					Items: []v1alpha1.Site{
-						{
-							ObjectMeta: v1.ObjectMeta{
-								Name:      "the-site",
-								Namespace: "test",
-							},
-						},
-					},
-				},
-				&v1alpha1.Link{
-					ObjectMeta: v1.ObjectMeta{
-						Name:      "my-link",
-						Namespace: "test",
-					},
-				},
-			},
-			expectedErrors: []string{"timeout is not valid: value 0 is not allowed"},
-		},
-		{
-			name:  "timeout is not valid because it is not a number",
-			args:  []string{"my-link"},
-			flags: common.CommandLinkDeleteFlags{Timeout: "one"},
+			flags: common.CommandLinkDeleteFlags{Timeout: time.Second * 0},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.SiteList{
 					Items: []v1alpha1.Site{
@@ -176,7 +129,7 @@ func TestCmdLinkDelete_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			expectedErrors: []string{"timeout is not valid: strconv.Atoi: parsing \"one\": invalid syntax"},
+			expectedErrors: []string{"timeout is not valid: duration must not be less than 10s; got 0s"},
 		},
 	}
 
@@ -202,14 +155,14 @@ func TestCmdLinkDelete_InputToOptions(t *testing.T) {
 	type test struct {
 		name            string
 		flags           common.CommandLinkDeleteFlags
-		expectedTimeout int
+		expectedTimeout time.Duration
 	}
 
 	testTable := []test{
 		{
 			name:            "check options",
-			flags:           common.CommandLinkDeleteFlags{"60"},
-			expectedTimeout: 60,
+			flags:           common.CommandLinkDeleteFlags{time.Minute},
+			expectedTimeout: time.Minute,
 		},
 	}
 
@@ -276,7 +229,7 @@ func TestCmdLinkDelete_Run(t *testing.T) {
 func TestCmdLinkDelete_WaitUntil(t *testing.T) {
 	type test struct {
 		name           string
-		timeout        int
+		timeout        time.Duration
 		skupperObjects []runtime.Object
 		expectError    bool
 	}
@@ -306,12 +259,12 @@ func TestCmdLinkDelete_WaitUntil(t *testing.T) {
 					},
 				},
 			},
-			timeout:     1,
+			timeout:     time.Second,
 			expectError: true,
 		},
 		{
 			name:        "link is deleted",
-			timeout:     1,
+			timeout:     time.Second,
 			expectError: false,
 		},
 	}
diff --git a/internal/cmd/skupper/link/kube/link_generate_test.go b/internal/cmd/skupper/link/kube/link_generate_test.go
index afd4c5a7..ee52c337 100644
--- a/internal/cmd/skupper/link/kube/link_generate_test.go
+++ b/internal/cmd/skupper/link/kube/link_generate_test.go
@@ -2,6 +2,7 @@ package kube
 
 import (
 	"testing"
+	"time"
 
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
@@ -28,7 +29,7 @@ func TestCmdLinkGenerate_ValidateInput(t *testing.T) {
 		{
 			name:           "link yaml is not generated because there is no site in the namespace.",
 			expectedErrors: []string{"there is no skupper site in this namespace", "there is no active skupper site in this namespace"},
-			flags:          common.CommandLinkGenerateFlags{Cost: "1", TlsSecret: "secret", Timeout: "60"},
+			flags:          common.CommandLinkGenerateFlags{Cost: "1", TlsSecret: "secret", Timeout: time.Minute},
 		},
 		{
 			name: "arguments were specified and they are not needed",
@@ -73,7 +74,7 @@ func TestCmdLinkGenerate_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			flags:          common.CommandLinkGenerateFlags{Cost: "1", TlsSecret: "secret", Timeout: "60"},
+			flags:          common.CommandLinkGenerateFlags{Cost: "1", TlsSecret: "secret", Timeout: time.Minute},
 			expectedErrors: []string{"arguments are not allowed in this command"},
 		},
 		{
@@ -118,7 +119,7 @@ func TestCmdLinkGenerate_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			flags:          common.CommandLinkGenerateFlags{Cost: "1", Timeout: "60"},
+			flags:          common.CommandLinkGenerateFlags{Cost: "1", Timeout: time.Minute},
 			expectedErrors: []string{"the TLS secret name was not specified"},
 		},
 		{
@@ -163,7 +164,7 @@ func TestCmdLinkGenerate_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			flags:          common.CommandLinkGenerateFlags{Cost: "one", TlsSecret: "secret", Timeout: "60"},
+			flags:          common.CommandLinkGenerateFlags{Cost: "one", TlsSecret: "secret", Timeout: time.Minute},
 			expectedErrors: []string{"link cost is not valid: strconv.Atoi: parsing \"one\": invalid syntax"},
 		},
 		{
@@ -208,7 +209,7 @@ func TestCmdLinkGenerate_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			flags: common.CommandLinkGenerateFlags{Cost: "-4", TlsSecret: "secret", Timeout: "60"},
+			flags: common.CommandLinkGenerateFlags{Cost: "-4", TlsSecret: "secret", Timeout: time.Minute},
 			expectedErrors: []string{
 				"link cost is not valid: value is not positive",
 			},
@@ -255,7 +256,7 @@ func TestCmdLinkGenerate_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			flags: common.CommandLinkGenerateFlags{Cost: "1", TlsSecret: "secret", Output: "not-valid", Timeout: "60"},
+			flags: common.CommandLinkGenerateFlags{Cost: "1", TlsSecret: "secret", Output: "not-valid", Timeout: time.Minute},
 			expectedErrors: []string{
 				"output type is not valid: value not-valid not allowed. It should be one of this options: [json yaml]",
 			},
@@ -302,7 +303,7 @@ func TestCmdLinkGenerate_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			flags: common.CommandLinkGenerateFlags{Cost: "1", TlsSecret: "tls secret", Timeout: "60"},
+			flags: common.CommandLinkGenerateFlags{Cost: "1", TlsSecret: "tls secret", Timeout: time.Minute},
 			expectedErrors: []string{
 				"the name of the tls secret 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])*)*$",
 			},
@@ -349,56 +350,9 @@ func TestCmdLinkGenerate_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			flags: common.CommandLinkGenerateFlags{Cost: "1", TlsSecret: "secret", Timeout: "0"},
+			flags: common.CommandLinkGenerateFlags{Cost: "1", TlsSecret: "secret", Timeout: time.Second * 0},
 			expectedErrors: []string{
-				"timeout is not valid: value 0 is not allowed",
-			},
-		},
-		{
-			name: "timeout is not valid because it is not a number",
-			skupperObjects: []runtime.Object{
-				&v1alpha1.SiteList{
-					Items: []v1alpha1.Site{
-						{
-							ObjectMeta: v1.ObjectMeta{
-								Name:      "the-site",
-								Namespace: "test",
-							},
-							Status: v1alpha1.SiteStatus{
-								Status: v1alpha1.Status{
-									StatusMessage: "OK",
-									Conditions: []v1.Condition{
-										{
-											Message:            "OK",
-											ObservedGeneration: 1,
-											Reason:             "OK",
-											Status:             "True",
-											Type:               "Running",
-										},
-										{
-											Message:            "OK",
-											ObservedGeneration: 1,
-											Reason:             "OK",
-											Status:             "True",
-											Type:               "Resolved",
-										},
-										{
-											Message:            "OK",
-											ObservedGeneration: 1,
-											Reason:             "OK",
-											Status:             "True",
-											Type:               "Configured",
-										},
-									},
-								},
-							},
-						},
-					},
-				},
-			},
-			flags: common.CommandLinkGenerateFlags{Cost: "1", TlsSecret: "secret", Timeout: "two"},
-			expectedErrors: []string{
-				"timeout is not valid: strconv.Atoi: parsing \"two\": invalid syntax",
+				"timeout is not valid: duration must not be less than 10s; got 0s",
 			},
 		},
 	}
@@ -437,7 +391,7 @@ func TestCmdLinkGenerate_InputToOptions(t *testing.T) {
 	testTable := []test{
 		{
 			name:                        "check options",
-			flags:                       common.CommandLinkGenerateFlags{"secret", "1", "json", false, "60"},
+			flags:                       common.CommandLinkGenerateFlags{"secret", "1", "json", false, time.Minute},
 			expectedCost:                1,
 			expectedTlsSecret:           "secret",
 			expectedOutput:              "json",
@@ -445,7 +399,7 @@ func TestCmdLinkGenerate_InputToOptions(t *testing.T) {
 		},
 		{
 			name:  "credentials are not needed",
-			flags: common.CommandLinkGenerateFlags{"", "1", "json", true, "60"},
+			flags: common.CommandLinkGenerateFlags{"", "1", "json", true, time.Minute},
 			activeSite: &v1alpha1.Site{
 
 				ObjectMeta: v1.ObjectMeta{
@@ -804,7 +758,7 @@ func TestCmdLinkGenerate_WaitUntil(t *testing.T) {
 		generatedLink      v1alpha1.Link
 		outputType         string
 		tlsSecret          string
-		timeout            int
+		timeout            time.Duration
 		k8sObjects         []runtime.Object
 		skupperObjects     []runtime.Object
 		skupperError       string
@@ -815,7 +769,7 @@ func TestCmdLinkGenerate_WaitUntil(t *testing.T) {
 		{
 			name:               "secret is not returned",
 			outputType:         "yaml",
-			timeout:            1,
+			timeout:            time.Second,
 			tlsSecret:          "linkSecret",
 			generateCredential: true,
 			expectError:        true,
@@ -824,21 +778,21 @@ func TestCmdLinkGenerate_WaitUntil(t *testing.T) {
 			name:               "the output only contains the link",
 			generateCredential: false,
 			outputType:         "yaml",
-			timeout:            1,
+			timeout:            time.Second,
 			expectError:        false,
 		},
 		{
 			name:               "bad format for the output",
 			generateCredential: false,
 			outputType:         "not supported",
-			timeout:            1,
+			timeout:            time.Second,
 			expectError:        true,
 		},
 		{
 			name:               "the output contains the link and the secret",
 			generateCredential: true,
 			outputType:         "yaml",
-			timeout:            1,
+			timeout:            time.Second,
 			tlsSecret:          "link-test",
 			k8sObjects: []runtime.Object{
 				&corev1.Secret{
@@ -872,7 +826,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:            1,
+			timeout:            time.Second,
 			tlsSecret:          "link-test",
 			k8sObjects: []runtime.Object{
 				&corev1.Secret{
diff --git a/internal/cmd/skupper/link/kube/link_update_test.go b/internal/cmd/skupper/link/kube/link_update_test.go
index ffcbae41..b97a2684 100644
--- a/internal/cmd/skupper/link/kube/link_update_test.go
+++ b/internal/cmd/skupper/link/kube/link_update_test.go
@@ -10,6 +10,7 @@ import (
 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"testing"
+	"time"
 )
 
 func TestCmdLinkUpdate_ValidateInput(t *testing.T) {
@@ -26,7 +27,7 @@ func TestCmdLinkUpdate_ValidateInput(t *testing.T) {
 		{
 			name:  "link is not updated because there is no site in the namespace.",
 			args:  []string{"my-link"},
-			flags: common.CommandLinkUpdateFlags{Cost: "1", Timeout: "60"},
+			flags: common.CommandLinkUpdateFlags{Cost: "1", Timeout: time.Minute},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Link{
 					ObjectMeta: v1.ObjectMeta{
@@ -40,7 +41,7 @@ func TestCmdLinkUpdate_ValidateInput(t *testing.T) {
 		{
 			name:  "link is not available",
 			args:  []string{"my-link"},
-			flags: common.CommandLinkUpdateFlags{Cost: "1", Timeout: "60"},
+			flags: common.CommandLinkUpdateFlags{Cost: "1", Timeout: time.Minute},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.SiteList{
 					Items: []v1alpha1.Site{
@@ -58,7 +59,7 @@ func TestCmdLinkUpdate_ValidateInput(t *testing.T) {
 		{
 			name:  "selected link does not exist",
 			args:  []string{"my"},
-			flags: common.CommandLinkUpdateFlags{Cost: "1", Timeout: "60"},
+			flags: common.CommandLinkUpdateFlags{Cost: "1", Timeout: time.Minute},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.SiteList{
 					Items: []v1alpha1.Site{
@@ -82,7 +83,7 @@ func TestCmdLinkUpdate_ValidateInput(t *testing.T) {
 		{
 			name:  "link name is not specified.",
 			args:  []string{},
-			flags: common.CommandLinkUpdateFlags{Cost: "1", Timeout: "60"},
+			flags: common.CommandLinkUpdateFlags{Cost: "1", Timeout: time.Minute},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.SiteList{
 					Items: []v1alpha1.Site{
@@ -106,7 +107,7 @@ func TestCmdLinkUpdate_ValidateInput(t *testing.T) {
 		{
 			name:  "more than one argument was specified",
 			args:  []string{"my", "link"},
-			flags: common.CommandLinkUpdateFlags{Cost: "1", Timeout: "60"},
+			flags: common.CommandLinkUpdateFlags{Cost: "1", Timeout: time.Minute},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.SiteList{
 					Items: []v1alpha1.Site{
@@ -130,7 +131,7 @@ func TestCmdLinkUpdate_ValidateInput(t *testing.T) {
 		{
 			name:  "Cost is not valid.",
 			args:  []string{"my-link"},
-			flags: common.CommandLinkUpdateFlags{Cost: "one", Timeout: "60"},
+			flags: common.CommandLinkUpdateFlags{Cost: "one", Timeout: time.Minute},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.SiteList{
 					Items: []v1alpha1.Site{
@@ -154,7 +155,7 @@ func TestCmdLinkUpdate_ValidateInput(t *testing.T) {
 		{
 			name:  "Cost is not positive",
 			args:  []string{"my-link"},
-			flags: common.CommandLinkUpdateFlags{Cost: "-4", Timeout: "60"},
+			flags: common.CommandLinkUpdateFlags{Cost: "-4", Timeout: time.Minute},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.SiteList{
 					Items: []v1alpha1.Site{
@@ -180,7 +181,7 @@ func TestCmdLinkUpdate_ValidateInput(t *testing.T) {
 		{
 			name:  "output format is not valid",
 			args:  []string{"my-link"},
-			flags: common.CommandLinkUpdateFlags{Cost: "1", Output: "not-valid", Timeout: "60"},
+			flags: common.CommandLinkUpdateFlags{Cost: "1", Output: "not-valid", Timeout: time.Minute},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.SiteList{
 					Items: []v1alpha1.Site{
@@ -206,7 +207,7 @@ func TestCmdLinkUpdate_ValidateInput(t *testing.T) {
 		{
 			name:  "tls secret not available",
 			args:  []string{"my-link"},
-			flags: common.CommandLinkUpdateFlags{Cost: "1", TlsSecret: "secret", Timeout: "60"},
+			flags: common.CommandLinkUpdateFlags{Cost: "1", TlsSecret: "secret", Timeout: time.Minute},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.SiteList{
 					Items: []v1alpha1.Site{
@@ -232,7 +233,7 @@ func TestCmdLinkUpdate_ValidateInput(t *testing.T) {
 		{
 			name:  "Timeout value is 0",
 			args:  []string{"my-link"},
-			flags: common.CommandLinkUpdateFlags{Cost: "1", TlsSecret: "secret", Timeout: "0"},
+			flags: common.CommandLinkUpdateFlags{Cost: "1", TlsSecret: "secret", Timeout: time.Second * 0},
 			k8sObjects: []runtime.Object{
 				&v12.Secret{
 					ObjectMeta: v1.ObjectMeta{
@@ -260,75 +261,7 @@ func TestCmdLinkUpdate_ValidateInput(t *testing.T) {
 				},
 			},
 			expectedErrors: []string{
-				"timeout is not valid: value 0 is not allowed",
-			},
-		},
-		{
-			name:  "Timeout value is negative",
-			args:  []string{"my-link"},
-			flags: common.CommandLinkUpdateFlags{Cost: "1", TlsSecret: "secret", Timeout: "-4"},
-			k8sObjects: []runtime.Object{
-				&v12.Secret{
-					ObjectMeta: v1.ObjectMeta{
-						Name:      "secret",
-						Namespace: "test",
-					},
-				},
-			},
-			skupperObjects: []runtime.Object{
-				&v1alpha1.SiteList{
-					Items: []v1alpha1.Site{
-						{
-							ObjectMeta: v1.ObjectMeta{
-								Name:      "the-site",
-								Namespace: "test",
-							},
-						},
-					},
-				},
-				&v1alpha1.Link{
-					ObjectMeta: v1.ObjectMeta{
-						Name:      "my-link",
-						Namespace: "test",
-					},
-				},
-			},
-			expectedErrors: []string{
-				"timeout is not valid: value is not positive",
-			},
-		},
-		{
-			name:  "Timeout value is not a number",
-			args:  []string{"my-link"},
-			flags: common.CommandLinkUpdateFlags{Cost: "1", TlsSecret: "secret", Timeout: "four"},
-			k8sObjects: []runtime.Object{
-				&v12.Secret{
-					ObjectMeta: v1.ObjectMeta{
-						Name:      "secret",
-						Namespace: "test",
-					},
-				},
-			},
-			skupperObjects: []runtime.Object{
-				&v1alpha1.SiteList{
-					Items: []v1alpha1.Site{
-						{
-							ObjectMeta: v1.ObjectMeta{
-								Name:      "the-site",
-								Namespace: "test",
-							},
-						},
-					},
-				},
-				&v1alpha1.Link{
-					ObjectMeta: v1.ObjectMeta{
-						Name:      "my-link",
-						Namespace: "test",
-					},
-				},
-			},
-			expectedErrors: []string{
-				"timeout is not valid: strconv.Atoi: parsing \"four\": invalid syntax",
+				"timeout is not valid: duration must not be less than 10s; got 0s",
 			},
 		},
 	}
@@ -360,18 +293,18 @@ func TestCmdLinkUpdate_InputToOptions(t *testing.T) {
 		expectedTlsSecret string
 		expectedCost      int
 		expectedOutput    string
-		expectedTimeout   int
+		expectedTimeout   time.Duration
 	}
 
 	testTable := []test{
 		{
 			name:              "check options",
 			args:              []string{"my-link"},
-			flags:             common.CommandLinkUpdateFlags{"secret", "1", "json", "60"},
+			flags:             common.CommandLinkUpdateFlags{"secret", "1", "json", time.Minute},
 			expectedCost:      1,
 			expectedTlsSecret: "secret",
 			expectedOutput:    "json",
-			expectedTimeout:   60,
+			expectedTimeout:   time.Minute,
 		},
 	}
 
@@ -489,7 +422,7 @@ func TestCmdLinkUpdate_WaitUntil(t *testing.T) {
 		skupperErrorMessage string
 		linkName            string
 		output              string
-		timeout             int
+		timeout             time.Duration
 		expectError         bool
 	}
 
@@ -506,20 +439,20 @@ func TestCmdLinkUpdate_WaitUntil(t *testing.T) {
 				},
 			},
 			linkName:    "my-link",
-			timeout:     1,
+			timeout:     time.Second,
 			expectError: true,
 		},
 		{
 			name:        "link is not returned",
 			linkName:    "my-link",
-			timeout:     1,
+			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:     1,
+			timeout:     time.Second,
 			expectError: false,
 		},
 		{
@@ -547,7 +480,7 @@ func TestCmdLinkUpdate_WaitUntil(t *testing.T) {
 				},
 			},
 			linkName:    "my-link",
-			timeout:     1,
+			timeout:     time.Second,
 			expectError: false,
 		},
 	}
diff --git a/internal/cmd/skupper/link/link_test.go b/internal/cmd/skupper/link/link_test.go
index 66afa3d0..9ddc563c 100644
--- a/internal/cmd/skupper/link/link_test.go
+++ b/internal/cmd/skupper/link/link_test.go
@@ -26,7 +26,7 @@ func TestCmdLinkFactory(t *testing.T) {
 				common.FlagNameCost:               "1",
 				common.FlagNameOutput:             "yaml",
 				common.FlagNameGenerateCredential: "true",
-				common.FlagNameTimeout:            "60",
+				common.FlagNameTimeout:            "1m0s",
 			},
 			command: CmdLinkGenerateFactory(types.PlatformKubernetes),
 		},
@@ -36,7 +36,7 @@ func TestCmdLinkFactory(t *testing.T) {
 				common.FlagNameTlsSecret: "",
 				common.FlagNameCost:      "1",
 				common.FlagNameOutput:    "",
-				common.FlagNameTimeout:   "60",
+				common.FlagNameTimeout:   "1m0s",
 			},
 			command: CmdLinkUpdateFactory(types.PlatformKubernetes),
 		},
@@ -50,7 +50,7 @@ func TestCmdLinkFactory(t *testing.T) {
 		{
 			name: "CmdLinkDeleteFactory",
 			expectedFlagsWithDefaultValue: map[string]interface{}{
-				common.FlagNameTimeout: "60",
+				common.FlagNameTimeout: "1m0s",
 			},
 			command: CmdLinkDeleteFactory(types.PlatformKubernetes),
 		},
diff --git a/internal/cmd/skupper/site/kube/site_create_test.go b/internal/cmd/skupper/site/kube/site_create_test.go
index e07d966c..55d5fb2a 100644
--- a/internal/cmd/skupper/site/kube/site_create_test.go
+++ b/internal/cmd/skupper/site/kube/site_create_test.go
@@ -4,9 +4,9 @@ import (
 	"github.com/skupperproject/skupper/api/types"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
-	"testing"
-
 	fakeclient "github.com/skupperproject/skupper/internal/kube/client/fake"
+	"testing"
+	"time"
 
 	"github.com/skupperproject/skupper/pkg/apis/skupper/v1alpha1"
 	"gotest.tools/assert"
@@ -61,13 +61,19 @@ func TestCmdSiteCreate_ValidateInput(t *testing.T) {
 		{
 			name:           "service account name is not valid.",
 			args:           []string{"my-site"},
-			flags:          &common.CommandSiteCreateFlags{ServiceAccount: "not valid service account name"},
+			flags:          &common.CommandSiteCreateFlags{ServiceAccount: "not valid service account name", Timeout: time.Minute},
 			expectedErrors: []string{"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},
+			expectedErrors: []string{},
+		},
 		{
 			name:  "link access type is not valid",
 			args:  []string{"my-site"},
-			flags: &common.CommandSiteCreateFlags{LinkAccessType: "not-valid"},
+			flags: &common.CommandSiteCreateFlags{LinkAccessType: "not-valid", Timeout: time.Minute},
 			expectedErrors: []string{
 				"link access type is not valid: value not-valid not allowed. It should be one of this options: [route loadbalancer default]",
 				"for the site to work with this type of linkAccess, the --enable-link-access option must be set to true",
@@ -76,23 +82,31 @@ func TestCmdSiteCreate_ValidateInput(t *testing.T) {
 		{
 			name:  "output format is not valid",
 			args:  []string{"my-site"},
-			flags: &common.CommandSiteCreateFlags{Output: "not-valid"},
+			flags: &common.CommandSiteCreateFlags{Output: "not-valid", Timeout: time.Minute},
 			expectedErrors: []string{
 				"output type is not valid: value not-valid not allowed. It should be one of this options: [json yaml]",
 			},
 		},
 		{
-			name:           "bind host flag is not valid for this platform",
+			name:           "host flag is not valid for this platform",
 			args:           []string{"my-site"},
-			flags:          &common.CommandSiteCreateFlags{BindHost: "host"},
+			flags:          &common.CommandSiteCreateFlags{Host: "host", Timeout: time.Minute},
 			expectedErrors: []string{},
 		},
 		{
 			name:           "subject alternative names flag is not valid for this platform",
 			args:           []string{"my-site"},
-			flags:          &common.CommandSiteCreateFlags{SubjectAlternativeNames: []string{"test"}},
+			flags:          &common.CommandSiteCreateFlags{SubjectAlternativeNames: []string{"test"}, Timeout: time.Minute},
 			expectedErrors: []string{},
 		},
+		{
+			name:  "timeout is not valid",
+			args:  []string{"my-site"},
+			flags: &common.CommandSiteCreateFlags{Timeout: time.Second * 0},
+			expectedErrors: []string{
+				"timeout is not valid: duration must not be less than 10s; got 0s",
+			},
+		},
 	}
 
 	for _, test := range testTable {
@@ -132,6 +146,7 @@ func TestCmdSiteCreate_InputToOptions(t *testing.T) {
 		flags              common.CommandSiteCreateFlags
 		expectedLinkAccess string
 		expectedOutput     string
+		expectedTimeout    time.Duration
 	}
 
 	testTable := []test{
@@ -170,6 +185,14 @@ func TestCmdSiteCreate_InputToOptions(t *testing.T) {
 			expectedLinkAccess: "",
 			expectedOutput:     "yaml",
 		},
+		{
+			name:               "options with timeout",
+			args:               []string{"my-site"},
+			flags:              common.CommandSiteCreateFlags{Timeout: time.Second * 60},
+			expectedLinkAccess: "",
+			expectedOutput:     "",
+			expectedTimeout:    time.Minute,
+		},
 	}
 
 	for _, test := range testTable {
@@ -183,6 +206,7 @@ func TestCmdSiteCreate_InputToOptions(t *testing.T) {
 
 			assert.Check(t, cmd.output == test.expectedOutput)
 			assert.Check(t, cmd.linkAccessType == test.expectedLinkAccess)
+			assert.Check(t, cmd.timeout == test.expectedTimeout)
 		})
 	}
 }
@@ -368,11 +392,13 @@ func TestCmdSiteCreate_WaitUntil(t *testing.T) {
 			Namespace: "test",
 		}
 
+		utils.SetRetryProfile(utils.TestRetryProfile)
 		fakeSkupperClient, err := fakeclient.NewFakeClient(command.Namespace, test.k8sObjects, test.skupperObjects, test.skupperError)
 		assert.Assert(t, err)
 		command.Client = fakeSkupperClient.GetSkupperClient().SkupperV1alpha1()
 		command.siteName = "my-site"
 		command.output = test.output
+		command.timeout = time.Second
 
 		t.Run(test.name, func(t *testing.T) {
 
diff --git a/internal/cmd/skupper/site/kube/site_delete_test.go b/internal/cmd/skupper/site/kube/site_delete_test.go
index 58137bf6..3694c9ea 100644
--- a/internal/cmd/skupper/site/kube/site_delete_test.go
+++ b/internal/cmd/skupper/site/kube/site_delete_test.go
@@ -1,8 +1,10 @@
 package kube
 
 import (
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"testing"
+	"time"
 
 	fakeclient "github.com/skupperproject/skupper/internal/kube/client/fake"
 
@@ -20,6 +22,7 @@ func TestCmdSiteDelete_ValidateInput(t *testing.T) {
 		skupperObjects []runtime.Object
 		expectedErrors []string
 		skupperError   string
+		flags          *common.CommandSiteDeleteFlags
 	}
 
 	testTable := []test{
@@ -192,6 +195,27 @@ func TestCmdSiteDelete_ValidateInput(t *testing.T) {
 			expectedErrors: []string{},
 			skupperError:   "",
 		},
+		{
+			name:       "timeout is not valid",
+			args:       []string{"my-site"},
+			flags:      &common.CommandSiteDeleteFlags{Timeout: time.Second * 0},
+			k8sObjects: nil,
+			skupperObjects: []runtime.Object{
+				&v1alpha1.Site{
+					ObjectMeta: v1.ObjectMeta{
+						Name:      "my-site",
+						Namespace: "test",
+					},
+					Status: v1alpha1.SiteStatus{
+						Status: v1alpha1.Status{
+							StatusMessage: "OK",
+						},
+					},
+				},
+			},
+			expectedErrors: []string{"timeout is not valid: duration must not be less than 10s; got 0s"},
+			skupperError:   "",
+		},
 	}
 
 	for _, test := range testTable {
@@ -206,6 +230,10 @@ func TestCmdSiteDelete_ValidateInput(t *testing.T) {
 
 			command.Client = fakeSkupperClient.GetSkupperClient().SkupperV1alpha1()
 
+			if test.flags != nil {
+				command.Flags = test.flags
+			}
+
 			actualErrors := command.ValidateInput(test.args)
 
 			actualErrorsMessages := utils.ErrorsToMessages(actualErrors)
@@ -216,6 +244,35 @@ func TestCmdSiteDelete_ValidateInput(t *testing.T) {
 	}
 }
 
+func TestCmdSiteDelete_InputToOptions(t *testing.T) {
+
+	type test struct {
+		name            string
+		flags           *common.CommandSiteDeleteFlags
+		expectedTimeout time.Duration
+	}
+
+	testTable := []test{
+		{
+			name:            "options with timeout",
+			flags:           &common.CommandSiteDeleteFlags{Timeout: time.Second * 30},
+			expectedTimeout: time.Second * 30,
+		},
+	}
+
+	for _, test := range testTable {
+		t.Run(test.name, func(t *testing.T) {
+			command := &CmdSiteDelete{
+				Namespace: "test",
+			}
+			command.Flags = test.flags
+			command.InputToOptions()
+
+			assert.Check(t, command.timeout == test.expectedTimeout)
+		})
+	}
+}
+
 func TestCmdSiteDelete_Run(t *testing.T) {
 	type test struct {
 		name           string
@@ -340,10 +397,13 @@ func TestCmdSiteDelete_WaitUntil(t *testing.T) {
 			Namespace: "test",
 		}
 
+		utils.SetRetryProfile(utils.TestRetryProfile)
 		fakeSkupperClient, err := fakeclient.NewFakeClient(command.Namespace, test.k8sObjects, test.skupperObjects, test.skupperError)
 		assert.Assert(t, err)
 		command.Client = fakeSkupperClient.GetSkupperClient().SkupperV1alpha1()
 		command.siteName = "my-site"
+		command.timeout = time.Second
+
 		t.Run(test.name, func(t *testing.T) {
 
 			err := command.WaitUntil()
diff --git a/internal/cmd/skupper/site/kube/site_update_test.go b/internal/cmd/skupper/site/kube/site_update_test.go
index 47881776..df394a60 100644
--- a/internal/cmd/skupper/site/kube/site_update_test.go
+++ b/internal/cmd/skupper/site/kube/site_update_test.go
@@ -5,6 +5,7 @@ import (
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"testing"
+	"time"
 
 	fakeclient "github.com/skupperproject/skupper/internal/kube/client/fake"
 	"github.com/skupperproject/skupper/pkg/apis/skupper/v1alpha1"
@@ -28,7 +29,7 @@ func TestCmdSiteUpdate_ValidateInput(t *testing.T) {
 		{
 			name:       "site is updated because there is already a site in the namespace.",
 			args:       []string{"my-site"},
-			flags:      &common.CommandSiteUpdateFlags{},
+			flags:      &common.CommandSiteUpdateFlags{Timeout: time.Minute},
 			k8sObjects: nil,
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Site{
@@ -49,7 +50,7 @@ func TestCmdSiteUpdate_ValidateInput(t *testing.T) {
 		{
 			name:       "site name is not specified.",
 			args:       []string{},
-			flags:      &common.CommandSiteUpdateFlags{},
+			flags:      &common.CommandSiteUpdateFlags{Timeout: time.Minute},
 			k8sObjects: nil,
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Site{
@@ -70,7 +71,7 @@ func TestCmdSiteUpdate_ValidateInput(t *testing.T) {
 		{
 			name:       "more than one argument was specified",
 			args:       []string{"my", "site"},
-			flags:      &common.CommandSiteUpdateFlags{},
+			flags:      &common.CommandSiteUpdateFlags{Timeout: time.Minute},
 			k8sObjects: nil,
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Site{
@@ -91,7 +92,7 @@ func TestCmdSiteUpdate_ValidateInput(t *testing.T) {
 		{
 			name:       "service account name is not valid.",
 			args:       []string{"my-site"},
-			flags:      &common.CommandSiteUpdateFlags{ServiceAccount: "not valid service account name"},
+			flags:      &common.CommandSiteUpdateFlags{ServiceAccount: "not valid service account name", Timeout: time.Minute},
 			k8sObjects: nil,
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Site{
@@ -112,7 +113,7 @@ func TestCmdSiteUpdate_ValidateInput(t *testing.T) {
 		{
 			name:  "host name was specified, but this flag does not work on kube platforms",
 			args:  []string{"my-site"},
-			flags: &common.CommandSiteUpdateFlags{BindHost: "host"},
+			flags: &common.CommandSiteUpdateFlags{BindHost: "host", Timeout: time.Minute},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Site{
 					ObjectMeta: v1.ObjectMeta{
@@ -131,7 +132,7 @@ func TestCmdSiteUpdate_ValidateInput(t *testing.T) {
 		{
 			name:       "link access type is not valid",
 			args:       []string{"my-site"},
-			flags:      &common.CommandSiteUpdateFlags{LinkAccessType: "not-valid"},
+			flags:      &common.CommandSiteUpdateFlags{LinkAccessType: "not-valid", Timeout: time.Minute},
 			k8sObjects: nil,
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Site{
@@ -155,7 +156,7 @@ func TestCmdSiteUpdate_ValidateInput(t *testing.T) {
 		{
 			name:       "output format is not valid",
 			args:       []string{"my-site"},
-			flags:      &common.CommandSiteUpdateFlags{Output: "not-valid"},
+			flags:      &common.CommandSiteUpdateFlags{Output: "not-valid", Timeout: time.Minute},
 			k8sObjects: nil,
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Site{
@@ -177,7 +178,7 @@ func TestCmdSiteUpdate_ValidateInput(t *testing.T) {
 		{
 			name:           "there is no skupper site",
 			args:           []string{"my-site"},
-			flags:          &common.CommandSiteUpdateFlags{},
+			flags:          &common.CommandSiteUpdateFlags{Timeout: time.Minute},
 			k8sObjects:     nil,
 			skupperObjects: nil,
 			skupperError:   "",
@@ -188,7 +189,7 @@ func TestCmdSiteUpdate_ValidateInput(t *testing.T) {
 		{
 			name:       "there are several skupper sites and no site name was specified",
 			args:       []string{},
-			flags:      &common.CommandSiteUpdateFlags{},
+			flags:      &common.CommandSiteUpdateFlags{Timeout: time.Minute},
 			k8sObjects: nil,
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Site{
@@ -220,7 +221,7 @@ func TestCmdSiteUpdate_ValidateInput(t *testing.T) {
 		{
 			name:       "there are several skupper sites but not the one specified by the user",
 			args:       []string{"special-site"},
-			flags:      &common.CommandSiteUpdateFlags{},
+			flags:      &common.CommandSiteUpdateFlags{Timeout: time.Minute},
 			k8sObjects: nil,
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Site{
@@ -252,7 +253,7 @@ func TestCmdSiteUpdate_ValidateInput(t *testing.T) {
 		{
 			name:       "there are several skupper sites and the user specifies one of them",
 			args:       []string{"my-site"},
-			flags:      &common.CommandSiteUpdateFlags{},
+			flags:      &common.CommandSiteUpdateFlags{Timeout: time.Minute},
 			k8sObjects: nil,
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Site{
@@ -284,7 +285,7 @@ func TestCmdSiteUpdate_ValidateInput(t *testing.T) {
 		{
 			name:       "the name specified in the arguments does not match with the current site",
 			args:       []string{"a-site"},
-			flags:      &common.CommandSiteUpdateFlags{},
+			flags:      &common.CommandSiteUpdateFlags{Timeout: time.Minute},
 			k8sObjects: nil,
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Site{
@@ -304,6 +305,28 @@ func TestCmdSiteUpdate_ValidateInput(t *testing.T) {
 				"site with name \"a-site\" is not available",
 			},
 		},
+		{
+			name:       "timeout format is not valid",
+			args:       []string{"my-site"},
+			flags:      &common.CommandSiteUpdateFlags{Timeout: time.Second * 0},
+			k8sObjects: nil,
+			skupperObjects: []runtime.Object{
+				&v1alpha1.Site{
+					ObjectMeta: v1.ObjectMeta{
+						Name:      "my-site",
+						Namespace: "test",
+					},
+					Status: v1alpha1.SiteStatus{
+						Status: v1alpha1.Status{
+							StatusMessage: "OK",
+						},
+					},
+				},
+			},
+			expectedErrors: []string{
+				"timeout is not valid: duration must not be less than 10s; got 0s",
+			},
+		},
 	}
 
 	for _, test := range testTable {
@@ -338,61 +361,47 @@ func TestCmdSiteUpdate_InputToOptions(t *testing.T) {
 		name               string
 		args               []string
 		flags              common.CommandSiteUpdateFlags
-		expectedSettings   map[string]string
 		expectedLinkAccess string
 		expectedOutput     string
+		expectedTimeout    time.Duration
 	}
 
 	testTable := []test{
 		{
-			name:  "options without link access enabled",
-			args:  []string{"my-site"},
-			flags: common.CommandSiteUpdateFlags{},
-			expectedSettings: map[string]string{
-				"name": "my-site",
-			},
+			name:               "options without link access enabled",
+			args:               []string{"my-site"},
+			flags:              common.CommandSiteUpdateFlags{},
 			expectedLinkAccess: "none",
 			expectedOutput:     "",
 		},
 		{
-			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},
-			expectedSettings: map[string]string{
-				"name": "my-site",
-			},
+			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:     "",
 		},
 		{
-			name:  "options with link access enabled using the nodeport type",
-			args:  []string{"my-site"},
-			flags: common.CommandSiteUpdateFlags{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:              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"},
-			expectedSettings: map[string]string{
-				"name": "my-site",
-			},
+			name:               "options with link access options not well specified",
+			args:               []string{"my-site"},
+			flags:              common.CommandSiteUpdateFlags{EnableLinkAccess: false, LinkAccessType: "nodeport"},
 			expectedLinkAccess: "none",
 			expectedOutput:     "",
 		},
 		{
-			name:  "options output type",
-			args:  []string{"my-site"},
-			flags: common.CommandSiteUpdateFlags{EnableLinkAccess: false, LinkAccessType: "nodeport", Output: "yaml"},
-			expectedSettings: map[string]string{
-				"name": "my-site",
-			},
+			name:               "options with output type and timeout",
+			args:               []string{"my-site"},
+			flags:              common.CommandSiteUpdateFlags{EnableLinkAccess: false, LinkAccessType: "nodeport", Output: "yaml", Timeout: time.Minute},
 			expectedLinkAccess: "none",
 			expectedOutput:     "yaml",
+			expectedTimeout:    time.Minute,
 		},
 	}
 
@@ -410,8 +419,6 @@ func TestCmdSiteUpdate_InputToOptions(t *testing.T) {
 
 			command.InputToOptions()
 
-			assert.DeepEqual(t, command.options, test.expectedSettings)
-
 			assert.Check(t, command.output == test.expectedOutput)
 		})
 	}
@@ -426,7 +433,6 @@ func TestCmdSiteUpdate_Run(t *testing.T) {
 		siteName           string
 		serviceAccountName string
 		linkAccessType     string
-		options            map[string]string
 		output             string
 		errorMessage       string
 	}
@@ -451,7 +457,6 @@ func TestCmdSiteUpdate_Run(t *testing.T) {
 			siteName:           "my-site",
 			serviceAccountName: "my-service-account",
 			linkAccessType:     "default",
-			options:            map[string]string{"name": "my-site"},
 			output:             "",
 			skupperError:       "",
 			errorMessage:       "",
@@ -463,7 +468,6 @@ func TestCmdSiteUpdate_Run(t *testing.T) {
 			siteName:           "my-site",
 			serviceAccountName: "my-service-account",
 			linkAccessType:     "default",
-			options:            map[string]string{"name": "my-site"},
 			output:             "",
 			skupperError:       "error",
 			errorMessage:       "error",
@@ -475,7 +479,6 @@ func TestCmdSiteUpdate_Run(t *testing.T) {
 			siteName:           "my-site",
 			serviceAccountName: "my-service-account",
 			linkAccessType:     "default",
-			options:            map[string]string{"name": "my-site"},
 			output:             "yaml",
 			skupperError:       "",
 			errorMessage:       "sites.skupper.io \"my-site\" not found",
@@ -487,7 +490,6 @@ func TestCmdSiteUpdate_Run(t *testing.T) {
 			siteName:           "my-site",
 			serviceAccountName: "my-service-account",
 			linkAccessType:     "default",
-			options:            map[string]string{"name": "my-site"},
 			output:             "unsupported",
 			skupperError:       "",
 			errorMessage:       "sites.skupper.io \"my-site\" not found",
@@ -505,7 +507,6 @@ func TestCmdSiteUpdate_Run(t *testing.T) {
 		command.siteName = test.siteName
 		command.serviceAccountName = test.serviceAccountName
 		command.linkAccessType = test.linkAccessType
-		command.options = test.options
 		command.output = test.output
 
 		t.Run(test.name, func(t *testing.T) {
@@ -561,10 +562,12 @@ func TestCmdSiteUpdate_WaitUntil(t *testing.T) {
 			Namespace: "test",
 		}
 
+		utils.SetRetryProfile(utils.TestRetryProfile)
 		fakeSkupperClient, err := fakeclient.NewFakeClient(command.Namespace, test.k8sObjects, test.skupperObjects, test.skupperError)
 		assert.Assert(t, err)
 		command.Client = fakeSkupperClient.GetSkupperClient().SkupperV1alpha1()
 		command.siteName = test.siteName
+		command.timeout = 1
 
 		t.Run(test.name, func(t *testing.T) {
 
diff --git a/internal/cmd/skupper/site/site_test.go b/internal/cmd/skupper/site/site_test.go
index 3f3ff5ad..a9f62996 100644
--- a/internal/cmd/skupper/site/site_test.go
+++ b/internal/cmd/skupper/site/site_test.go
@@ -25,6 +25,7 @@ func TestCmdSiteFactory(t *testing.T) {
 				common.FlagNameLinkAccessType:          "",
 				common.FlagNameServiceAccount:          "",
 				common.FlagNameOutput:                  "",
+				common.FlagNameTimeout:                 "1m0s",
 				common.FlagNameBindHost:                "",
 				common.FlagNameSubjectAlternativeNames: "[]",
 			},
@@ -38,14 +39,17 @@ func TestCmdSiteFactory(t *testing.T) {
 				common.FlagNameServiceAccount:          "",
 				common.FlagNameOutput:                  "",
 				common.FlagNameBindHost:                "",
+				common.FlagNameTimeout:                 "1m0s",
 				common.FlagNameSubjectAlternativeNames: "[]",
 			},
 			command: CmdSiteUpdateFactory(types.PlatformKubernetes),
 		},
 		{
-			name:                          "CmdSiteDeleteFactory",
-			expectedFlagsWithDefaultValue: map[string]interface{}{},
-			command:                       CmdSiteDeleteFactory(types.PlatformKubernetes),
+			name: "CmdSiteDeleteFactory",
+			expectedFlagsWithDefaultValue: map[string]interface{}{
+				common.FlagNameTimeout: "1m0s",
+			},
+			command: CmdSiteDeleteFactory(types.PlatformKubernetes),
 		},
 		{
 			name:                          "CmdSiteStatusFactory",
diff --git a/pkg/utils/validator/simple_validator_test.go b/pkg/utils/validator/simple_validator_test.go
index 7b8b561e..f504a5cb 100644
--- a/pkg/utils/validator/simple_validator_test.go
+++ b/pkg/utils/validator/simple_validator_test.go
@@ -4,6 +4,7 @@ import (
 	"reflect"
 	"regexp"
 	"testing"
+	"time"
 
 	"gotest.tools/assert"
 )
@@ -87,17 +88,13 @@ func TestIntegerValidator_Evaluate(t *testing.T) {
 func TestTimeoutInSecondsValidator_Evaluate(t *testing.T) {
 	type test struct {
 		name   string
-		value  interface{}
+		value  time.Duration
 		result bool
 	}
 
 	testTable := []test{
-		{name: "empty string", value: "", result: false},
-		{name: "value greater than zero", value: 235, result: true},
-		{name: "zero value", value: 0, result: false},
-		{name: "negative number", value: -2, result: false},
-		{name: "not valid characters", value: "abc", result: false},
-		{name: "nil value", value: nil, result: false},
+		{name: "value less than minimum", value: time.Second * 1, result: false},
+		{name: "zero value", value: time.Second * 0, result: false},
 	}
 
 	for _, test := range testTable {