Repository: skupperproject/skupper
Pull request #2089 (2025-04-25 17:19:32)

Add system apply & delete commands
For context:
https://skupperproject.github.io/refdog/commands/system/apply.html
https://skupperproject.github.io/refdog/commands/system/delete.html

Examples on how to execute them:

```
skupper system apply -f resources.yaml
skupper link generate | skupper system delete -f -
```


Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 9e0d86cc..5b72ca3b 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -267,7 +267,7 @@ jobs:
             KUBECONFIG=~/kc.west ./scripts/kind-dev-cluster -n west  \
               --images ociarchive
       - run:
-          name: Initalize Sites
+          name: Initialize Sites
           command: |
             set -x
 
diff --git a/.codespellrc b/.codespellrc
index 913e38b2..a1d7e66c 100644
--- a/.codespellrc
+++ b/.codespellrc
@@ -3,4 +3,4 @@
 skip = .git*,go.sum,.codespellrc
 check-hidden = true
 # ignore-regex = 
-ignore-words-list = dne,haa
+ignore-words-list = dne
diff --git a/Dockerfile.network-observer b/Dockerfile.network-observer
index 38278b88..890fba30 100644
--- a/Dockerfile.network-observer
+++ b/Dockerfile.network-observer
@@ -24,7 +24,7 @@ RUN yarn install && yarn build
 FROM --platform=$TARGETPLATFORM registry.access.redhat.com/ubi9-minimal
 LABEL \
   org.opencontainers.image.title="Skupper Network Observer" \
-  org.opencontainers.image.description="Exposes Skupper network telemetry thorugh an API, metrics and a web console"
+  org.opencontainers.image.description="Exposes Skupper network telemetry through an API, metrics and a web console"
 
 # Create user and group and switch to user's context
 RUN microdnf -y install shadow-utils \
diff --git a/charts/network-observer/README.md b/charts/network-observer/README.md
index 7b605744..a77a72ad 100644
--- a/charts/network-observer/README.md
+++ b/charts/network-observer/README.md
@@ -12,7 +12,7 @@ package manager.
 
 ## Prerequisites
 
-- A Skupper Version 2 Site running in the same Kuberentes Namesapce the network
+- A Skupper Version 2 Site running in the same Kubernetes Namespace the network
 observer is to be installed in.
 - The Skupper Controller running and managing the Site.
 
diff --git a/charts/network-observer/templates/NOTES.txt b/charts/network-observer/templates/NOTES.txt
index b2121d85..ef96339c 100644
--- a/charts/network-observer/templates/NOTES.txt
+++ b/charts/network-observer/templates/NOTES.txt
@@ -3,7 +3,7 @@ You have installed the skupper network observer!
 Accessing the console:
 {{- if .Values.ingress.enabled }}
   The {{ include "network-observer.fullname" . }} service is exposed through an
-  Ingress and should be availalble external to the cluster at the following
+  Ingress and should be available external to the cluster at the following
   locations.
 {{- range $host := .Values.ingress.hosts }}
   {{- range .paths }}
diff --git a/cmd/kube-adaptor/main.go b/cmd/kube-adaptor/main.go
index 82c21f7c..a1eac5de 100644
--- a/cmd/kube-adaptor/main.go
+++ b/cmd/kube-adaptor/main.go
@@ -52,7 +52,7 @@ func main() {
 	var configDir string
 	var configMapName string
 	iflag.StringVar(flags, &configDir, "config-dir", "SKUPPER_CONFIG_DIR", "/etc/skupper-router-certs", "The directory to which configuration should be saved")
-	iflag.StringVar(flags, &configMapName, "router-config", "SKUPPER_ROUTER_CONFIG", "skupper-router", "The name of the ConfigMap containg the router config")
+	iflag.StringVar(flags, &configMapName, "router-config", "SKUPPER_ROUTER_CONFIG", "skupper-router", "The name of the ConfigMap containing the router config")
 
 	// if -version used, report and exit
 	isVersion := flags.Bool("version", false, "Report the version of Config Sync")
diff --git a/cmd/network-observer/internal/cmd/commands.go b/cmd/network-observer/internal/cmd/commands.go
index bd782620..cd30f92c 100644
--- a/cmd/network-observer/internal/cmd/commands.go
+++ b/cmd/network-observer/internal/cmd/commands.go
@@ -59,7 +59,7 @@ func runEnsureSecret(args []string) error {
 	}
 	iflag.StringVar(flags, &namespace, "namespace", "NAMESPACE", "", "The Kubernetes namespace scope for the controller")
 	iflag.StringVar(flags, &kubeconfig, "kubeconfig", "KUBECONFIG", "", "A path to the kubeconfig file to use")
-	iflag.StringVar(flags, &format, "format", "ENSURE_SECRET_FORMAT", "", "Secret format. One of [oauth2-proxy-session-cookie, htpasswd]. Requried.")
+	iflag.StringVar(flags, &format, "format", "ENSURE_SECRET_FORMAT", "", "Secret format. One of [oauth2-proxy-session-cookie, htpasswd]. Required.")
 	flags.Parse(args[1:])
 	posArgs := flags.Args()
 	if len(posArgs) != 1 {
@@ -75,7 +75,7 @@ func runEnsureSecret(args []string) error {
 	case "oauth2-proxy-session-cookie":
 		secretsProvider = generateOauth2ProxySessionSecret
 	case "":
-		fmt.Fprintf(flags.Output(), "flag --format is requried\n")
+		fmt.Fprintf(flags.Output(), "flag --format is required\n")
 		flags.Usage()
 		os.Exit(1)
 	default:
diff --git a/cmd/network-observer/internal/collector/collector.go b/cmd/network-observer/internal/collector/collector.go
index ef09d720..fd8ed017 100644
--- a/cmd/network-observer/internal/collector/collector.go
+++ b/cmd/network-observer/internal/collector/collector.go
@@ -341,7 +341,7 @@ func (c *Collector) discoveryHandler(ctx context.Context) func(eventsource.Info)
 		})
 
 		if err != nil {
-			c.logger.Error("error creating watcher for discoverd source", slog.Any("error", err))
+			c.logger.Error("error creating watcher for discovered source", slog.Any("error", err))
 			c.discovery.Forget(source.ID)
 			return
 		}
diff --git a/cmd/network-observer/internal/collector/connections.go b/cmd/network-observer/internal/collector/connections.go
index 36571b11..91018aef 100644
--- a/cmd/network-observer/internal/collector/connections.go
+++ b/cmd/network-observer/internal/collector/connections.go
@@ -456,7 +456,7 @@ func (c *connectionManager) run(ctx context.Context) {
 				c.transportFlows.Matching(func(state *transportState) bool {
 					// only reconcile flow sources for flows that are not yet
 					// reconcilled, but have been waiting at least 15s (a delay
-					// for any related records to propogate before perminately
+					// for any related records to propagate before perminately
 					// associating a flow with an inferred source)
 					return state.metrics == nil &&
 						time.Since(state.FirstSeen) > 15*time.Second
diff --git a/cmd/network-observer/internal/collector/processes.go b/cmd/network-observer/internal/collector/processes.go
index 80e4a810..b607d2b8 100644
--- a/cmd/network-observer/internal/collector/processes.go
+++ b/cmd/network-observer/internal/collector/processes.go
@@ -165,7 +165,7 @@ func (m *processManager) run(ctx context.Context) func() error {
 
 								}
 								if toDelete != "" {
-									m.logger.Info("Deleting site server process superceeded by new process",
+									m.logger.Info("Deleting site server process superseded by new process",
 										slog.String("id", toDelete),
 										slog.String("site_id", siteID),
 										slog.String("host", host),
diff --git a/cmd/network-observer/spec/openapi.yaml b/cmd/network-observer/spec/openapi.yaml
index eb334c54..67cef5f5 100644
--- a/cmd/network-observer/spec/openapi.yaml
+++ b/cmd/network-observer/spec/openapi.yaml
@@ -5,7 +5,7 @@ info:
   description: >-
     The Skupper network observer exposes a read only HTTP API. This API is used
     by the network console frontend to display information about a skupper
-    netwrok.
+    network.
   title: Skupper Network Observer HTTP API.
   license:
     name: Apache 2.0
diff --git a/doc/adr/0003-show-incoming-links-as-optional.md b/doc/adr/0003-show-incoming-links-as-optional.md
index 0eca0709..abba6df3 100644
--- a/doc/adr/0003-show-incoming-links-as-optional.md
+++ b/doc/adr/0003-show-incoming-links-as-optional.md
@@ -4,7 +4,7 @@ Date: 2023-05-10
 
 ## Status
 
-Superceded by [5. showing incoming liks by default](0005-showing-incoming-links-by-default.md)
+Superseded by [5. showing incoming liks by default](0005-showing-incoming-links-by-default.md)
 
 ## Context
 
diff --git a/doc/adr/0004-move-claims-redemption-to-router-deployment.md b/doc/adr/0004-move-claims-redemption-to-router-deployment.md
index ed2a261c..58fef91a 100644
--- a/doc/adr/0004-move-claims-redemption-to-router-deployment.md
+++ b/doc/adr/0004-move-claims-redemption-to-router-deployment.md
@@ -10,7 +10,7 @@ Proposal
 
 Currently claims tokens are converted into certificates (if the claim
 is valid) by code embedded in the service-controller. This means that
-this functionaly us exposed through a different service and ingress
+this functionally us exposed through a different service and ingress
 resource than the certs tokens are used with. This can cause confusion
 with configuration where the ingress mode of the console is what is
 relevant to the claims.
diff --git a/doc/adr/0006-explicit-resources-for-declaring-service-bindings.md b/doc/adr/0006-explicit-resources-for-declaring-service-bindings.md
index 85a5c334..ee2f59b2 100644
--- a/doc/adr/0006-explicit-resources-for-declaring-service-bindings.md
+++ b/doc/adr/0006-explicit-resources-for-declaring-service-bindings.md
@@ -38,7 +38,7 @@ the Service, no target is inferred. The service is configured to point
 at the router pods on a port that will route to the address specified
 by the skupper.io/address annotation.
 
-The code accummulated to handle all these configurations as well as
+The code accumulated to handle all these configurations as well as
 the CLI is not the easiest to maintain.
 
 ## Decision
diff --git a/doc/adr/0008-combine-site-controller-and-service-controller.md b/doc/adr/0008-combine-site-controller-and-service-controller.md
index f537dea7..b3710963 100644
--- a/doc/adr/0008-combine-site-controller-and-service-controller.md
+++ b/doc/adr/0008-combine-site-controller-and-service-controller.md
@@ -52,7 +52,7 @@ Skupper will also use one less pod per site in many cases.
 
 The get command and associated web api will no longer be available in
 its current form. Neither will the already deprecated web api for
-managing links and tokens. The prefered approach for making
+managing links and tokens. The preferred approach for making
 information available will be through kubernetes resources. An
 alternative to `skupper debug events` will be needed. This might be
 Kubernetes Event resources or some other resource into which
diff --git a/internal/cmd/skupper/common/constants.go b/internal/cmd/skupper/common/constants.go
index 5a04ce73..95cfe394 100644
--- a/internal/cmd/skupper/common/constants.go
+++ b/internal/cmd/skupper/common/constants.go
@@ -11,14 +11,16 @@ var (
 )
 
 const (
-	Connectors     string = "Connector"
-	Listeners      string = "Listener"
-	Sites          string = "Site"
-	RouterAccesses string = "RouterAccess"
-	Links          string = "Link"
-	AccessTokens   string = "AccessToken"
-	Secrets        string = "Secret"
-	ConfigMaps     string = "ConfigMap"
+	Connectors      string = "Connector"
+	Listeners       string = "Listener"
+	Sites           string = "Site"
+	RouterAccesses  string = "RouterAccess"
+	Links           string = "Link"
+	AccessTokens    string = "AccessToken"
+	Secrets         string = "Secret"
+	ConfigMaps      string = "ConfigMap"
+	Certificates    string = "Certificate"
+	SecuredAccesses string = "SecuredAccess"
 )
 
 const (
diff --git a/internal/cmd/skupper/common/flags.go b/internal/cmd/skupper/common/flags.go
index 423f54b6..cfd5fdb6 100644
--- a/internal/cmd/skupper/common/flags.go
+++ b/internal/cmd/skupper/common/flags.go
@@ -86,6 +86,9 @@ for other Kubernetes flavors, loadbalancer is the default.`
 
 	FlagNameHA = "enable-ha"
 	FlagDescHA = "Configure the site for high availability (EnableHA). EnableHA sites have two active routers"
+
+	FlagNameFileName = "filename"
+	FlagDescFileName = "The name of the file with custom resources"
 )
 
 type CommandSiteCreateFlags struct {
@@ -254,3 +257,11 @@ type CommandSystemGenerateBundleFlags struct {
 	Input string
 	Type  string
 }
+
+type CommandSystemApplyFlags struct {
+	Filename string
+}
+
+type CommandSystemDeleteFlags struct {
+	Filename string
+}
diff --git a/internal/cmd/skupper/system/kube/system_apply.go b/internal/cmd/skupper/system/kube/system_apply.go
new file mode 100644
index 00000000..340e0e81
--- /dev/null
+++ b/internal/cmd/skupper/system/kube/system_apply.go
@@ -0,0 +1,37 @@
+package kube
+
+import (
+	"fmt"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	skupperv2alpha1 "github.com/skupperproject/skupper/pkg/generated/client/clientset/versioned/typed/skupper/v2alpha1"
+	"github.com/spf13/cobra"
+	"k8s.io/client-go/kubernetes"
+)
+
+type CmdSystemApply struct {
+	Client     skupperv2alpha1.SkupperV2alpha1Interface
+	KubeClient kubernetes.Interface
+	CobraCmd   *cobra.Command
+	Namespace  string
+	Flags      *common.CommandSystemApplyFlags
+}
+
+func NewCmdSystemApply() *CmdSystemApply {
+
+	skupperCmd := CmdSystemApply{}
+
+	return &skupperCmd
+}
+
+func (cmd *CmdSystemApply) NewClient(cobraCommand *cobra.Command, args []string) {}
+
+func (cmd *CmdSystemApply) ValidateInput(args []string) error { return nil }
+
+func (cmd *CmdSystemApply) InputToOptions() {}
+
+func (cmd *CmdSystemApply) Run() error {
+	fmt.Println("This command does not support kubernetes platforms.")
+	return nil
+}
+
+func (cmd *CmdSystemApply) WaitUntil() error { return nil }
diff --git a/internal/cmd/skupper/system/kube/system_delete.go b/internal/cmd/skupper/system/kube/system_delete.go
new file mode 100644
index 00000000..ad4f4ef5
--- /dev/null
+++ b/internal/cmd/skupper/system/kube/system_delete.go
@@ -0,0 +1,37 @@
+package kube
+
+import (
+	"fmt"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	skupperv2alpha1 "github.com/skupperproject/skupper/pkg/generated/client/clientset/versioned/typed/skupper/v2alpha1"
+	"github.com/spf13/cobra"
+	"k8s.io/client-go/kubernetes"
+)
+
+type CmdSystemDelete struct {
+	Client     skupperv2alpha1.SkupperV2alpha1Interface
+	KubeClient kubernetes.Interface
+	CobraCmd   *cobra.Command
+	Namespace  string
+	Flags      *common.CommandSystemDeleteFlags
+}
+
+func NewCmdSystemDelete() *CmdSystemDelete {
+
+	skupperCmd := CmdSystemDelete{}
+
+	return &skupperCmd
+}
+
+func (cmd *CmdSystemDelete) NewClient(cobraCommand *cobra.Command, args []string) {}
+
+func (cmd *CmdSystemDelete) ValidateInput(args []string) error { return nil }
+
+func (cmd *CmdSystemDelete) InputToOptions() {}
+
+func (cmd *CmdSystemDelete) Run() error {
+	fmt.Println("This command does not support kubernetes platforms.")
+	return nil
+}
+
+func (cmd *CmdSystemDelete) WaitUntil() error { return nil }
diff --git a/internal/cmd/skupper/system/nonkube/system_apply.go b/internal/cmd/skupper/system/nonkube/system_apply.go
new file mode 100644
index 00000000..d6c70bd6
--- /dev/null
+++ b/internal/cmd/skupper/system/nonkube/system_apply.go
@@ -0,0 +1,220 @@
+package nonkube
+
+import (
+	"bufio"
+	"errors"
+	"fmt"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/nonkube/client/fs"
+	skupperv2alpha1 "github.com/skupperproject/skupper/pkg/generated/client/clientset/versioned/typed/skupper/v2alpha1"
+	"github.com/spf13/cobra"
+	"k8s.io/client-go/kubernetes"
+	"log/slog"
+	"os"
+	"strings"
+)
+
+type CmdSystemApply struct {
+	Client               skupperv2alpha1.SkupperV2alpha1Interface
+	KubeClient           kubernetes.Interface
+	CobraCmd             *cobra.Command
+	Namespace            string
+	Flags                *common.CommandSystemApplyFlags
+	ParseInput           func(namespace string, reader *bufio.Reader, result *fs.InputFileResource) error
+	siteHandler          *fs.SiteHandler
+	connectorHandler     *fs.ConnectorHandler
+	listenerHandler      *fs.ListenerHandler
+	linkHandler          *fs.LinkHandler
+	routerAccessHandler  *fs.RouterAccessHandler
+	accessTokenHandler   *fs.AccessTokenHandler
+	certificateHandler   *fs.CertificateHandler
+	securedAccessHandler *fs.SecuredAccessHandler
+	secretHandler        *fs.SecretHandler
+	file                 string
+}
+
+func NewCmdSystemApply() *CmdSystemApply {
+
+	skupperCmd := CmdSystemApply{}
+
+	return &skupperCmd
+}
+
+func (cmd *CmdSystemApply) NewClient(cobraCommand *cobra.Command, args []string) {
+	if cmd.CobraCmd != nil && cmd.CobraCmd.Flag(common.FlagNameNamespace) != nil && cmd.CobraCmd.Flag(common.FlagNameNamespace).Value.String() != "" {
+		cmd.Namespace = cmd.CobraCmd.Flag(common.FlagNameNamespace).Value.String()
+	}
+
+	cmd.connectorHandler = fs.NewConnectorHandler(cmd.Namespace)
+	cmd.listenerHandler = fs.NewListenerHandler(cmd.Namespace)
+	cmd.linkHandler = fs.NewLinkHandler(cmd.Namespace)
+	cmd.routerAccessHandler = fs.NewRouterAccessHandler(cmd.Namespace)
+	cmd.accessTokenHandler = fs.NewAccessTokenHandler(cmd.Namespace)
+	cmd.siteHandler = fs.NewSiteHandler(cmd.Namespace)
+	cmd.secretHandler = fs.NewSecretHandler(cmd.Namespace)
+	cmd.certificateHandler = fs.NewCertificateHandler(cmd.Namespace)
+	cmd.securedAccessHandler = fs.NewSecuredAccessHandler(cmd.Namespace)
+	cmd.ParseInput = fs.ParseInput
+}
+
+func (cmd *CmdSystemApply) ValidateInput(args []string) error {
+	var validationErrors []error
+
+	if len(args) > 0 {
+		validationErrors = append(validationErrors, fmt.Errorf("This command does not accept arguments"))
+	}
+
+	if cmd.Flags == nil || cmd.Flags.Filename == "" {
+		validationErrors = append(validationErrors, fmt.Errorf("You need to provide a file to apply or use standard input.\n Example: cat site.yaml | skupper system apply -f -"))
+	}
+
+	if cmd.Flags != nil && cmd.Flags.Filename != "" && cmd.Flags.Filename != "-" {
+
+		if !strings.HasSuffix(cmd.Flags.Filename, ".yaml") && !strings.HasSuffix(cmd.Flags.Filename, ".json") {
+			validationErrors = append(validationErrors, fmt.Errorf("The file has an unsupported extension, it should have one of the following: .yaml, .json"))
+		}
+
+		info, err := os.Stat(cmd.Flags.Filename)
+		if os.IsNotExist(err) {
+			validationErrors = append(validationErrors, fmt.Errorf("The file %q does not exist", cmd.Flags.Filename))
+		} else if err != nil {
+			validationErrors = append(validationErrors, fmt.Errorf("Error while accessing the file: %s", err))
+		}
+
+		if err == nil && info.IsDir() {
+			validationErrors = append(validationErrors, fmt.Errorf("The file %q is a directory", cmd.Flags.Filename))
+		}
+	}
+
+	return errors.Join(validationErrors...)
+}
+
+func (cmd *CmdSystemApply) InputToOptions() {
+	cmd.file = cmd.Flags.Filename
+	if cmd.Flags.Filename == "-" {
+		cmd.file = ""
+	}
+	if cmd.Namespace == "" {
+		cmd.Namespace = "default"
+	}
+}
+
+func (cmd *CmdSystemApply) Run() error {
+
+	//read the file or the pipe stream
+	inputReader := cmd.CobraCmd.InOrStdin()
+	crApplied := false
+
+	if cmd.file != "" {
+		file, err := os.Open(cmd.file)
+		if err != nil {
+			return fmt.Errorf("Error while opening the file: %s", err)
+		}
+		inputReader = file
+	}
+
+	//get custom resources from the input
+	parsedInput := &fs.InputFileResource{}
+	err := cmd.ParseInput(cmd.Namespace, bufio.NewReader(inputReader), parsedInput)
+	if err != nil {
+		return fmt.Errorf("Failed parsing the custom resources: %s", err)
+	}
+
+	for _, site := range parsedInput.Site {
+		err := cmd.siteHandler.Add(site)
+		if err != nil {
+			slog.Error("Error while adding site", slog.String("site", site.Name), slog.Any("error", err))
+		} else {
+			crApplied = true
+			fmt.Printf("Site %s added\n", site.Name)
+		}
+	}
+
+	for _, connector := range parsedInput.Connector {
+		err := cmd.connectorHandler.Add(connector)
+		if err != nil {
+			slog.Error("Error while adding connector", slog.String("connector", connector.Name), slog.Any("error", err))
+		} else {
+			crApplied = true
+			fmt.Printf("Connector %s added\n", connector.Name)
+		}
+	}
+
+	for _, listener := range parsedInput.Listener {
+		err := cmd.listenerHandler.Add(listener)
+		if err != nil {
+			slog.Error("Error while adding listener", slog.String("listener", listener.Name), slog.Any("error", err))
+		} else {
+			crApplied = true
+			fmt.Printf("Listener %s added\n", listener.Name)
+		}
+	}
+
+	for _, link := range parsedInput.Link {
+		err := cmd.linkHandler.Add(link)
+		if err != nil {
+			slog.Error("Error while adding link", slog.String("link", link.Name), slog.Any("error", err))
+		} else {
+			crApplied = true
+			fmt.Printf("Link %s added\n", link.Name)
+		}
+	}
+
+	for _, routerAccess := range parsedInput.RouterAccess {
+		err := cmd.routerAccessHandler.Add(routerAccess)
+		if err != nil {
+			slog.Error("Error while adding router access", slog.String("router access", routerAccess.Name), slog.Any("error", err))
+		} else {
+			crApplied = true
+			fmt.Printf("RouterAccess %s added\n", routerAccess.Name)
+		}
+	}
+
+	for _, accessToken := range parsedInput.AccessToken {
+		err := cmd.accessTokenHandler.Add(accessToken)
+		if err != nil {
+			slog.Error("Error while adding access token", slog.String("access token", accessToken.Name), slog.Any("error", err))
+		} else {
+			crApplied = true
+			fmt.Printf("AccessToken %s added\n", accessToken.Name)
+		}
+	}
+
+	for _, secret := range parsedInput.Secret {
+		err := cmd.secretHandler.Add(secret)
+		if err != nil {
+			slog.Error("Error while adding secret", slog.String("secret", secret.Name), slog.Any("error", err))
+		} else {
+			crApplied = true
+			fmt.Printf("Secret %s added\n", secret.Name)
+		}
+	}
+
+	for _, securedAccess := range parsedInput.SecuredAccess {
+		err := cmd.securedAccessHandler.Add(securedAccess)
+		if err != nil {
+			slog.Error("Error while adding secured access", slog.String("secured access", securedAccess.Name), slog.Any("error", err))
+		} else {
+			crApplied = true
+			fmt.Printf("SecuredAccess %s added\n", securedAccess.Name)
+		}
+	}
+
+	for _, certificate := range parsedInput.Certificate {
+		err := cmd.certificateHandler.Add(certificate)
+		if err != nil {
+			slog.Error("Error while adding certificate", slog.String("certificate", certificate.Name), slog.Any("error", err))
+		} else {
+			crApplied = true
+			fmt.Printf("Certificate %s added\n", certificate.Name)
+		}
+	}
+
+	if crApplied {
+		fmt.Println("Custom resources are applied. You can now run `skupper system reload` to make effective the changes.")
+	}
+
+	return nil
+}
+
+func (cmd *CmdSystemApply) WaitUntil() error { return nil }
diff --git a/internal/cmd/skupper/system/nonkube/system_delete.go b/internal/cmd/skupper/system/nonkube/system_delete.go
new file mode 100644
index 00000000..07580697
--- /dev/null
+++ b/internal/cmd/skupper/system/nonkube/system_delete.go
@@ -0,0 +1,239 @@
+package nonkube
+
+import (
+	"bufio"
+	"errors"
+	"fmt"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/nonkube/client/fs"
+	skupperv2alpha1 "github.com/skupperproject/skupper/pkg/generated/client/clientset/versioned/typed/skupper/v2alpha1"
+	"github.com/spf13/cobra"
+	"k8s.io/client-go/kubernetes"
+	"log/slog"
+	"os"
+	"strings"
+)
+
+type CmdSystemDelete struct {
+	Client               skupperv2alpha1.SkupperV2alpha1Interface
+	KubeClient           kubernetes.Interface
+	CobraCmd             *cobra.Command
+	Namespace            string
+	Flags                *common.CommandSystemDeleteFlags
+	ParseInput           func(namespace string, reader *bufio.Reader, result *fs.InputFileResource) error
+	siteHandler          *fs.SiteHandler
+	connectorHandler     *fs.ConnectorHandler
+	listenerHandler      *fs.ListenerHandler
+	linkHandler          *fs.LinkHandler
+	routerAccessHandler  *fs.RouterAccessHandler
+	accessTokenHandler   *fs.AccessTokenHandler
+	certificateHandler   *fs.CertificateHandler
+	securedAccessHandler *fs.SecuredAccessHandler
+	secretHandler        *fs.SecretHandler
+	file                 string
+}
+
+func NewCmdSystemDelete() *CmdSystemDelete {
+
+	skupperCmd := CmdSystemDelete{}
+
+	return &skupperCmd
+}
+
+func (cmd *CmdSystemDelete) NewClient(cobraCommand *cobra.Command, args []string) {
+	if cmd.CobraCmd != nil && cmd.CobraCmd.Flag(common.FlagNameNamespace) != nil && cmd.CobraCmd.Flag(common.FlagNameNamespace).Value.String() != "" {
+		cmd.Namespace = cmd.CobraCmd.Flag(common.FlagNameNamespace).Value.String()
+	}
+
+	cmd.connectorHandler = fs.NewConnectorHandler(cmd.Namespace)
+	cmd.listenerHandler = fs.NewListenerHandler(cmd.Namespace)
+	cmd.linkHandler = fs.NewLinkHandler(cmd.Namespace)
+	cmd.routerAccessHandler = fs.NewRouterAccessHandler(cmd.Namespace)
+	cmd.accessTokenHandler = fs.NewAccessTokenHandler(cmd.Namespace)
+	cmd.siteHandler = fs.NewSiteHandler(cmd.Namespace)
+	cmd.secretHandler = fs.NewSecretHandler(cmd.Namespace)
+	cmd.certificateHandler = fs.NewCertificateHandler(cmd.Namespace)
+	cmd.securedAccessHandler = fs.NewSecuredAccessHandler(cmd.Namespace)
+	cmd.ParseInput = fs.ParseInput
+}
+
+func (cmd *CmdSystemDelete) ValidateInput(args []string) error {
+	var validationErrors []error
+
+	if len(args) > 0 {
+		validationErrors = append(validationErrors, fmt.Errorf("This command does not accept arguments"))
+	}
+
+	if cmd.Flags == nil || cmd.Flags.Filename == "" {
+		validationErrors = append(validationErrors, fmt.Errorf("You need to provide a file to delete custom resources or use standard input.\n Example: cat site.yaml | skupper system delete -f -"))
+	}
+
+	if cmd.Flags != nil && cmd.Flags.Filename != "" && cmd.Flags.Filename != "-" {
+
+		if !strings.HasSuffix(cmd.Flags.Filename, ".yaml") && !strings.HasSuffix(cmd.Flags.Filename, ".json") {
+			validationErrors = append(validationErrors, fmt.Errorf("The file has an unsupported extension, it should have one of the following: .yaml, .json"))
+		}
+
+		info, err := os.Stat(cmd.Flags.Filename)
+		if os.IsNotExist(err) {
+			validationErrors = append(validationErrors, fmt.Errorf("The file %q does not exist", cmd.Flags.Filename))
+		} else if err != nil {
+			validationErrors = append(validationErrors, fmt.Errorf("Error while accessing the file: %s", err))
+		}
+
+		if err == nil && info.IsDir() {
+			validationErrors = append(validationErrors, fmt.Errorf("The file %q is a directory", cmd.Flags.Filename))
+		}
+	}
+
+	return errors.Join(validationErrors...)
+}
+
+func (cmd *CmdSystemDelete) InputToOptions() {
+	cmd.file = cmd.Flags.Filename
+	if cmd.Flags.Filename == "-" {
+		cmd.file = ""
+	}
+	if cmd.Namespace == "" {
+		cmd.Namespace = "default"
+	}
+}
+
+func (cmd *CmdSystemDelete) Run() error {
+	//read the file or the pipe stream
+	inputReader := cmd.CobraCmd.InOrStdin()
+	crDeleted := false
+
+	if cmd.file != "" {
+		file, err := os.Open(cmd.file)
+		if err != nil {
+			return fmt.Errorf("Error while opening the file: %s", err)
+		}
+		inputReader = file
+	}
+
+	//get custom resources from the input
+	parsedInput := &fs.InputFileResource{}
+	err := cmd.ParseInput(cmd.Namespace, bufio.NewReader(inputReader), parsedInput)
+	if err != nil {
+		return fmt.Errorf("Failed parsing the custom resources: %s", err)
+	}
+
+	for _, site := range parsedInput.Site {
+		if site.Name != "" {
+			err := cmd.siteHandler.Delete(site.Name)
+			if err != nil {
+				slog.Error("Error while deleting site", slog.String("site", site.Name), slog.Any("error", err))
+			} else {
+				crDeleted = true
+				fmt.Printf("Site %s deleted\n", site.Name)
+			}
+		}
+	}
+
+	for _, connector := range parsedInput.Connector {
+		if connector.Name != "" {
+			err := cmd.connectorHandler.Delete(connector.Name)
+			if err != nil {
+				slog.Error("Error while deleting connector", slog.String("connector", connector.Name), slog.Any("error", err))
+			} else {
+				crDeleted = true
+				fmt.Printf("Connector %s deleted\n", connector.Name)
+			}
+		}
+	}
+
+	for _, listener := range parsedInput.Listener {
+		if listener.Name != "" {
+			err := cmd.listenerHandler.Delete(listener.Name)
+			if err != nil {
+				slog.Error("Error while deleting listener", slog.String("listener", listener.Name), slog.Any("error", err))
+			} else {
+				crDeleted = true
+				fmt.Printf("Listener %s deleted\n", listener.Name)
+			}
+		}
+	}
+
+	for _, link := range parsedInput.Link {
+		if link.Name != "" {
+			err := cmd.linkHandler.Delete(link.Name)
+			if err != nil {
+				slog.Error("Error while deleting link", slog.String("link", link.Name), slog.Any("error", err))
+			} else {
+				crDeleted = true
+				fmt.Printf("Link %s deleted\n", link.Name)
+			}
+
+		}
+	}
+
+	for _, routerAccess := range parsedInput.RouterAccess {
+		if routerAccess.Name != "" {
+			err := cmd.routerAccessHandler.Delete(routerAccess.Name)
+			if err != nil {
+				slog.Error("Error while deleting router access", slog.String("router access", routerAccess.Name), slog.Any("error", err))
+			} else {
+				crDeleted = true
+				fmt.Printf("RouterAccess %s deleted\n", routerAccess.Name)
+			}
+
+		}
+	}
+
+	for _, accessToken := range parsedInput.AccessToken {
+		if accessToken.Name != "" {
+			err := cmd.accessTokenHandler.Delete(accessToken.Name)
+			if err != nil {
+				slog.Error("Error while deleting access token", slog.String("access token", accessToken.Name), slog.Any("error", err))
+			} else {
+				crDeleted = true
+				fmt.Printf("AccessToken %s deleted\n", accessToken.Name)
+			}
+		}
+	}
+
+	for _, secret := range parsedInput.Secret {
+		if secret.Name != "" {
+			err := cmd.secretHandler.Delete(secret.Name)
+			if err != nil {
+				slog.Error("Error while deleting secret", slog.String("secret", secret.Name), slog.Any("error", err))
+			} else {
+				crDeleted = true
+				fmt.Printf("Secret %s deleted\n", secret.Name)
+			}
+		}
+	}
+
+	for _, certificate := range parsedInput.Certificate {
+		if certificate.Name != "" {
+			err := cmd.certificateHandler.Delete(certificate.Name)
+			if err != nil {
+				slog.Error("Error while deleting certificate", slog.String("certificate", certificate.Name), slog.Any("error", err))
+			} else {
+				crDeleted = true
+				fmt.Printf("Certificate %s deleted\n", certificate.Name)
+			}
+		}
+	}
+
+	for _, securedAccess := range parsedInput.SecuredAccess {
+		if securedAccess.Name != "" {
+			err := cmd.securedAccessHandler.Delete(securedAccess.Name)
+			if err != nil {
+				slog.Error("Error while deleting secured access", slog.String("secured access", securedAccess.Name), slog.Any("error", err))
+			} else {
+				crDeleted = true
+				fmt.Printf("SecuredAccess %s deleted\n", securedAccess.Name)
+			}
+		}
+	}
+
+	if crDeleted {
+		fmt.Println("Custom resources deleted. You can now run `skupper system reload` to make effective the changes.")
+	}
+
+	return nil
+}
+
+func (cmd *CmdSystemDelete) WaitUntil() error { return nil }
diff --git a/internal/cmd/skupper/system/nonkube/system_generate-bundle.go b/internal/cmd/skupper/system/nonkube/system_generate-bundle.go
index 22fad5df..980294d2 100644
--- a/internal/cmd/skupper/system/nonkube/system_generate-bundle.go
+++ b/internal/cmd/skupper/system/nonkube/system_generate-bundle.go
@@ -25,7 +25,7 @@ type CmdSystemGenerateBundle struct {
 	BundleName      string
 }
 
-func NewCmdCmdSystemGenerateBundle() *CmdSystemGenerateBundle {
+func NewCmdSystemGenerateBundle() *CmdSystemGenerateBundle {
 
 	skupperCmd := CmdSystemGenerateBundle{}
 
diff --git a/internal/cmd/skupper/system/system.go b/internal/cmd/skupper/system/system.go
index f683a2d5..e74ec992 100644
--- a/internal/cmd/skupper/system/system.go
+++ b/internal/cmd/skupper/system/system.go
@@ -33,6 +33,8 @@ approach, which is based on the new set of Custom Resource Definitions (CRDs).`,
 	cmd.AddCommand(CmdSystemInstallFactory(platform))
 	cmd.AddCommand(CmdSystemUnInstallFactory(platform))
 	cmd.AddCommand(CmdSystemGenerateBundleFactory(platform))
+	cmd.AddCommand(CmdSystemApplyFactory(platform))
+	cmd.AddCommand(CmdSystemDeleteFactory(platform))
 
 	return cmd
 }
@@ -146,7 +148,7 @@ func CmdSystemGenerateBundleFactory(configuredPlatform common.Platform) *cobra.C
 
 	//This implementation will warn the user that the command is not available for Kubernetes environments.
 	kubeCommand := kube.NewCmdCmdSystemGenerateBundle()
-	nonKubeCommand := nonkube.NewCmdCmdSystemGenerateBundle()
+	nonKubeCommand := nonkube.NewCmdSystemGenerateBundle()
 
 	cmdSystemGenerateBundleDesc := common.SkupperCmdDescription{
 		Use:   "generate-bundle <bundle-file>",
@@ -168,3 +170,57 @@ func CmdSystemGenerateBundleFactory(configuredPlatform common.Platform) *cobra.C
 
 	return cmd
 }
+
+func CmdSystemApplyFactory(configuredPlatform common.Platform) *cobra.Command {
+
+	//This implementation will warn the user that the command is not available for Kubernetes environments.
+	kubeCommand := kube.NewCmdSystemApply()
+	nonKubeCommand := nonkube.NewCmdSystemApply()
+
+	cmdSystemApplyDesc := common.SkupperCmdDescription{
+		Use:   "apply",
+		Short: "Create or update resources using files or standard input.",
+		Long:  "Create or update resources using files or standard input.",
+		Example: `skupper system apply -f ~/my-site.yaml
+skupper link generate | skupper system apply -f -`,
+	}
+
+	cmd := common.ConfigureCobraCommand(configuredPlatform, cmdSystemApplyDesc, kubeCommand, nonKubeCommand)
+
+	cmdFlags := common.CommandSystemApplyFlags{}
+
+	cmd.Flags().StringVarP(&cmdFlags.Filename, common.FlagNameFileName, "f", "", common.FlagDescFileName)
+
+	kubeCommand.CobraCmd = cmd
+	kubeCommand.Flags = &cmdFlags
+	nonKubeCommand.CobraCmd = cmd
+	nonKubeCommand.Flags = &cmdFlags
+
+	return cmd
+}
+
+func CmdSystemDeleteFactory(configuredPlatform common.Platform) *cobra.Command {
+
+	//This implementation will warn the user that the command is not available for Kubernetes environments.
+	kubeCommand := kube.NewCmdSystemDelete()
+	nonKubeCommand := nonkube.NewCmdSystemDelete()
+
+	cmdSystemDeleteDesc := common.SkupperCmdDescription{
+		Use:   "delete",
+		Short: "Delete resources using files or standard input.",
+		Long:  "Delete resources using files or standard input.",
+	}
+
+	cmd := common.ConfigureCobraCommand(configuredPlatform, cmdSystemDeleteDesc, kubeCommand, nonKubeCommand)
+
+	cmdFlags := common.CommandSystemDeleteFlags{}
+
+	cmd.Flags().StringVarP(&cmdFlags.Filename, common.FlagNameFileName, "f", "", common.FlagDescFileName)
+
+	kubeCommand.CobraCmd = cmd
+	kubeCommand.Flags = &cmdFlags
+	nonKubeCommand.CobraCmd = cmd
+	nonKubeCommand.Flags = &cmdFlags
+
+	return cmd
+}
diff --git a/internal/flag/flag.go b/internal/flag/flag.go
index 031a54f0..519b7904 100644
--- a/internal/flag/flag.go
+++ b/internal/flag/flag.go
@@ -14,14 +14,14 @@ func StringVar(flags *flag.FlagSet, output *string, flagName string, envVarName
 
 func BoolVar(flags *flag.FlagSet, output *bool, flagName string, envVarName string, defaultValue bool, usage string) error {
 	dval, err := boolEnvVar(envVarName, defaultValue)
-	//set flag inspite of error, caller can decide whether to ignore and go with default or not
+	//set flag in spite of error, caller can decide whether to ignore and go with default or not
 	flags.BoolVar(output, flagName, dval, usage)
 	return err
 }
 
 func IntVar(flags *flag.FlagSet, output *int, flagName string, envVarName string, defaultValue int, usage string) error {
 	dval, err := intEnvVar(envVarName, defaultValue)
-	//set flag inspite of error, caller can decide whether to ignore and go with default or not
+	//set flag in spite of error, caller can decide whether to ignore and go with default or not
 	flags.IntVar(output, flagName, dval, usage)
 	return err
 }
diff --git a/internal/flow/status.go b/internal/flow/status.go
index dbedfb0b..2e68b336 100644
--- a/internal/flow/status.go
+++ b/internal/flow/status.go
@@ -406,7 +406,7 @@ func (s *StatusSync) handleDiscovery(source eventsource.Info) {
 	})
 
 	if err != nil {
-		s.logger.Error("error creating watcher for discoverd source", slog.Any("error", err))
+		s.logger.Error("error creating watcher for discovered source", slog.Any("error", err))
 		s.discovery.Forget(source.ID)
 		return
 	}
diff --git a/internal/kube/certificates/mgr.go b/internal/kube/certificates/mgr.go
index 55ce6542..503ebe43 100644
--- a/internal/kube/certificates/mgr.go
+++ b/internal/kube/certificates/mgr.go
@@ -1,5 +1,5 @@
 // Package certificates provides the ability to create or update
-// instances of the v2alpha1 Certificate resource, and esnure that a
+// instances of the v2alpha1 Certificate resource, and ensure that a
 // corresponding Secret resource is maintained for each.
 package certificates
 
diff --git a/internal/kube/client/client.go b/internal/kube/client/client.go
index ff518939..66442301 100644
--- a/internal/kube/client/client.go
+++ b/internal/kube/client/client.go
@@ -20,8 +20,8 @@ import (
 	skupperclient "github.com/skupperproject/skupper/pkg/generated/client/clientset/versioned"
 )
 
-// The Clients interface defines acess to different types of client
-// interface required for interactions withe the Kubernetes API
+// The Clients interface defines access to different types of client
+// interface required for interactions with the Kubernetes API
 // server.
 type Clients interface {
 	GetKubeClient() kubernetes.Interface
diff --git a/internal/kube/grants/redeem.go b/internal/kube/grants/redeem.go
index 202f4a36..97af8988 100644
--- a/internal/kube/grants/redeem.go
+++ b/internal/kube/grants/redeem.go
@@ -28,7 +28,7 @@ func RedeemAccessToken(token *skupperv2alpha1.AccessToken, site *skupperv2alpha1
 	if err != nil {
 		return updateAccessTokenStatus(token, err, clients)
 	}
-	log.Printf("HTTP Post to %s for %s/%s was sucessful, decoding response body", token.Spec.Url, token.Namespace, token.Name)
+	log.Printf("HTTP Post to %s for %s/%s was successful, decoding response body", token.Spec.Url, token.Namespace, token.Name)
 	return handleTokenResponse(body, token, site, clients)
 }
 
diff --git a/internal/kube/grants/tokens.go b/internal/kube/grants/tokens.go
index a2a5f9ba..f003616a 100644
--- a/internal/kube/grants/tokens.go
+++ b/internal/kube/grants/tokens.go
@@ -45,7 +45,7 @@ func NewTokenGenerator(site *skupperv2alpha1.Site, clients internalclient.Client
 	}
 	if err := generator.loadCA(site.DefaultIssuer()); err != nil {
 		log.Printf("Error retrieving default issuer %s for site %s in %s: %s", site.DefaultIssuer(), site.Name, site.Namespace, err)
-		return nil, errors.New("Could not get issuer for requested certficate")
+		return nil, errors.New("Could not get issuer for requested certificate")
 	}
 	if ok := generator.setValidHostsFromSite(site); !ok {
 		log.Printf("Could not resolve any target endpoints for site %s in %s", site.Name, site.Namespace)
diff --git a/internal/kube/watchers/watchers.go b/internal/kube/watchers/watchers.go
index fbce6a9e..75aa27f5 100644
--- a/internal/kube/watchers/watchers.go
+++ b/internal/kube/watchers/watchers.go
@@ -193,7 +193,7 @@ func (c *EventProcessor) Stop() {
 }
 
 // Creates an event handler that will take handle events from an
-// informer by contructing an appropriate ResourceChange instance and
+// informer by constructing an appropriate ResourceChange instance and
 // adding it to the EventProcessor's work queue.
 func (c *EventProcessor) newEventHandler(handler ResourceChangeHandler) *cache.ResourceEventHandlerFuncs {
 	evt := ResourceChange{
diff --git a/internal/nonkube/client/fs/access_token_handler.go b/internal/nonkube/client/fs/access_token_handler.go
new file mode 100644
index 00000000..07c7bfdc
--- /dev/null
+++ b/internal/nonkube/client/fs/access_token_handler.go
@@ -0,0 +1,51 @@
+package fs
+
+import (
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
+)
+
+type AccessTokenHandler struct {
+	BaseCustomResourceHandler
+	pathProvider PathProvider
+}
+
+func NewAccessTokenHandler(namespace string) *AccessTokenHandler {
+	return &AccessTokenHandler{
+		pathProvider: PathProvider{
+			Namespace: namespace,
+		},
+	}
+}
+
+func (a *AccessTokenHandler) Add(resource v2alpha1.AccessToken) error {
+
+	fileName := resource.Name + ".yaml"
+	content, err := a.EncodeToYaml(resource)
+	if err != nil {
+		return err
+	}
+
+	err = a.WriteFile(a.pathProvider.GetNamespace(), fileName, content, common.AccessTokens)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (a *AccessTokenHandler) Get(name string, opt GetOptions) (*v2alpha1.AccessToken, error) {
+	return nil, nil
+}
+
+func (a *AccessTokenHandler) Delete(name string) error {
+	fileName := name + ".yaml"
+
+	if err := a.DeleteFile(a.pathProvider.GetNamespace(), fileName, common.AccessTokens); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (a *AccessTokenHandler) List() ([]*v2alpha1.AccessToken, error) { return nil, nil }
diff --git a/internal/nonkube/client/fs/certificate_handler.go b/internal/nonkube/client/fs/certificate_handler.go
new file mode 100644
index 00000000..5deba4b0
--- /dev/null
+++ b/internal/nonkube/client/fs/certificate_handler.go
@@ -0,0 +1,51 @@
+package fs
+
+import (
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
+)
+
+type CertificateHandler struct {
+	BaseCustomResourceHandler
+	pathProvider PathProvider
+}
+
+func NewCertificateHandler(namespace string) *CertificateHandler {
+	return &CertificateHandler{
+		pathProvider: PathProvider{
+			Namespace: namespace,
+		},
+	}
+}
+
+func (c *CertificateHandler) Add(resource v2alpha1.Certificate) error {
+
+	fileName := resource.Name + ".yaml"
+	content, err := c.EncodeToYaml(resource)
+	if err != nil {
+		return err
+	}
+
+	err = c.WriteFile(c.pathProvider.GetNamespace(), fileName, content, common.Certificates)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (c *CertificateHandler) Get(name string, opt GetOptions) (*v2alpha1.Certificate, error) {
+	return nil, nil
+}
+
+func (c *CertificateHandler) Delete(name string) error {
+	fileName := name + ".yaml"
+
+	if err := c.DeleteFile(c.pathProvider.GetNamespace(), fileName, common.Certificates); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (c *CertificateHandler) List() ([]*v2alpha1.Certificate, error) { return nil, nil }
diff --git a/internal/nonkube/client/fs/configmap_handler.go b/internal/nonkube/client/fs/configmap_handler.go
index 52f6c1d9..4c244b2d 100644
--- a/internal/nonkube/client/fs/configmap_handler.go
+++ b/internal/nonkube/client/fs/configmap_handler.go
@@ -76,7 +76,7 @@ func (s *ConfigMapHandler) Get(name string, opts GetOptions) (*corev1.ConfigMap,
 			return nil, err
 		}
 	} else {
-		// read from input directory to get lastest config
+		// read from input directory to get latest config
 		err, file := s.ReadFile(s.pathProvider.GetNamespace(), fileName, common.ConfigMaps)
 		if err != nil {
 			return nil, err
diff --git a/internal/nonkube/client/fs/connector_handler.go b/internal/nonkube/client/fs/connector_handler.go
index d1cd59fd..81e14043 100644
--- a/internal/nonkube/client/fs/connector_handler.go
+++ b/internal/nonkube/client/fs/connector_handler.go
@@ -1,8 +1,6 @@
 package fs
 
 import (
-	"errors"
-	"io/fs"
 	"os"
 
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
@@ -59,7 +57,7 @@ func (s *ConnectorHandler) Get(name string, opt GetOptions) (*v2alpha1.Connector
 			return nil, err
 		}
 	} else {
-		// read from input directory to get lastest config
+		// read from input directory to get latest config
 		err, file := s.ReadFile(s.pathProvider.GetNamespace(), fileName, common.Connectors)
 		if err != nil {
 			return nil, err
@@ -76,9 +74,7 @@ func (s *ConnectorHandler) Delete(name string) error {
 	fileName := name + ".yaml"
 
 	if err := s.DeleteFile(s.pathProvider.GetNamespace(), fileName, common.Connectors); err != nil {
-		if !errors.Is(err, fs.ErrNotExist) {
-			return err
-		}
+		return err
 	}
 
 	return nil
diff --git a/internal/nonkube/client/fs/custom_resource_handler.go b/internal/nonkube/client/fs/custom_resource_handler.go
index ab62eab7..546dd835 100644
--- a/internal/nonkube/client/fs/custom_resource_handler.go
+++ b/internal/nonkube/client/fs/custom_resource_handler.go
@@ -104,6 +104,11 @@ func (b *BaseCustomResourceHandler) DeleteFile(path string, name string, kind st
 		completeFilePath = path
 	}
 
+	_, err := os.Stat(completeFilePath)
+	if os.IsNotExist(err) {
+		return err
+	}
+
 	if err := os.RemoveAll(completeFilePath); err != nil {
 		return fmt.Errorf("failed to delete file: %s", err)
 	}
diff --git a/internal/nonkube/client/fs/input_parser.go b/internal/nonkube/client/fs/input_parser.go
new file mode 100644
index 00000000..bb20d1bb
--- /dev/null
+++ b/internal/nonkube/client/fs/input_parser.go
@@ -0,0 +1,124 @@
+package fs
+
+import (
+	"bufio"
+	"fmt"
+	"github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
+
+	"io"
+	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
+	yamlutil "k8s.io/apimachinery/pkg/util/yaml"
+	"log/slog"
+)
+
+type InputFileResource struct {
+	Site          []v2alpha1.Site
+	Listener      []v2alpha1.Listener
+	Connector     []v2alpha1.Connector
+	RouterAccess  []v2alpha1.RouterAccess
+	AccessGrant   []v2alpha1.AccessGrant
+	Link          []v2alpha1.Link
+	AccessToken   []v2alpha1.AccessToken
+	Certificate   []v2alpha1.Certificate
+	SecuredAccess []v2alpha1.SecuredAccess
+	Secret        []corev1.Secret
+}
+
+func ParseInput(namespace string, reader *bufio.Reader, result *InputFileResource) error {
+
+	var err error
+	logger := slog.New(slog.Default().Handler())
+	logInvalidResource := func(gvk *schema.GroupVersionKind) {
+		logger.Warn("invalid resource ignored:", slog.String("resource", gvk.String()))
+	}
+	yamlJsonDecoder := yamlutil.NewYAMLOrJSONDecoder(reader, 1024)
+	// allow reading multiple-document file
+	for {
+		var rawObj runtime.RawExtension
+		err = yamlJsonDecoder.Decode(&rawObj)
+		if err != nil {
+			if err != io.EOF {
+				return fmt.Errorf("error decoding file: %s", err)
+			}
+			break
+		}
+		// Decoded object from rawObject, with gvk (Group Version Kind)
+		obj, gvk, err := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme).Decode(rawObj.Raw, nil, nil)
+		if err != nil {
+			return err
+		}
+
+		if v2alpha1.SchemeGroupVersion == gvk.GroupVersion() {
+			switch gvk.Kind {
+			case "Site":
+				var site v2alpha1.Site
+				convertTo(obj, &site)
+				site.Namespace = namespace
+				result.Site = append(result.Site, site)
+			case "Listener":
+				var listener v2alpha1.Listener
+				convertTo(obj, &listener)
+				listener.Namespace = namespace
+				result.Listener = append(result.Listener, listener)
+			case "Connector":
+				var connector v2alpha1.Connector
+				convertTo(obj, &connector)
+				connector.Namespace = namespace
+				result.Connector = append(result.Connector, connector)
+			case "RouterAccess":
+				var routerAccess v2alpha1.RouterAccess
+				convertTo(obj, &routerAccess)
+				routerAccess.Namespace = namespace
+				result.RouterAccess = append(result.RouterAccess, routerAccess)
+			case "AccessGrant":
+				var grant v2alpha1.AccessGrant
+				convertTo(obj, &grant)
+				grant.Namespace = namespace
+				result.AccessGrant = append(result.AccessGrant, grant)
+			case "Link":
+				var link v2alpha1.Link
+				convertTo(obj, &link)
+				link.Namespace = namespace
+				result.Link = append(result.Link, link)
+			case "AccessToken":
+				var claim v2alpha1.AccessToken
+				convertTo(obj, &claim)
+				claim.Namespace = namespace
+				result.AccessToken = append(result.AccessToken, claim)
+			case "Certificate":
+				var certificate v2alpha1.Certificate
+				convertTo(obj, &certificate)
+				result.Certificate = append(result.Certificate, certificate)
+			case "SecuredAccess":
+				var securedAccess v2alpha1.SecuredAccess
+				convertTo(obj, &securedAccess)
+				securedAccess.Namespace = namespace
+				result.SecuredAccess = append(result.SecuredAccess, securedAccess)
+			default:
+				logInvalidResource(gvk)
+			}
+		} else if corev1.SchemeGroupVersion == gvk.GroupVersion() {
+			switch gvk.Kind {
+			case "Secret":
+				var secret corev1.Secret
+				convertTo(obj, &secret)
+				secret.Namespace = namespace
+				result.Secret = append(result.Secret, secret)
+			default:
+				logInvalidResource(gvk)
+			}
+		} else {
+			logInvalidResource(gvk)
+		}
+	}
+	return nil
+
+}
+
+func convertTo(obj runtime.Object, target interface{}) {
+	runtime.DefaultUnstructuredConverter.FromUnstructured(obj.(runtime.Unstructured).UnstructuredContent(), target)
+}
diff --git a/internal/nonkube/client/fs/link_handler.go b/internal/nonkube/client/fs/link_handler.go
index 0502466b..d400d09b 100644
--- a/internal/nonkube/client/fs/link_handler.go
+++ b/internal/nonkube/client/fs/link_handler.go
@@ -1,8 +1,6 @@
 package fs
 
 import (
-	"errors"
-	"io/fs"
 	"os"
 	"strings"
 
@@ -69,10 +67,8 @@ func (s *LinkHandler) Get(name string, opts GetOptions) (*v2alpha1.Link, error)
 func (s *LinkHandler) Delete(name string) error {
 	fileName := name + ".yaml"
 
-	if err := s.DeleteFile(s.pathProvider.GetRuntimeNamespace(), fileName, "links"); err != nil {
-		if !errors.Is(err, fs.ErrNotExist) {
-			return err
-		}
+	if err := s.DeleteFile(s.pathProvider.GetNamespace(), fileName, common.Links); err != nil {
+		return err
 	}
 
 	return nil
diff --git a/internal/nonkube/client/fs/listener_handler.go b/internal/nonkube/client/fs/listener_handler.go
index 35b4441f..dd12877d 100644
--- a/internal/nonkube/client/fs/listener_handler.go
+++ b/internal/nonkube/client/fs/listener_handler.go
@@ -1,8 +1,6 @@
 package fs
 
 import (
-	"errors"
-	"io/fs"
 	"os"
 
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
@@ -60,7 +58,7 @@ func (s *ListenerHandler) Get(name string, opts GetOptions) (*v2alpha1.Listener,
 			return nil, err
 		}
 	} else {
-		// read from input directory to get lastest config
+		// read from input directory to get latest config
 		err, file := s.ReadFile(s.pathProvider.GetNamespace(), fileName, common.Listeners)
 		if err != nil {
 			return nil, err
@@ -77,9 +75,7 @@ func (s *ListenerHandler) Delete(name string) error {
 	fileName := name + ".yaml"
 
 	if err := s.DeleteFile(s.pathProvider.GetNamespace(), fileName, common.Listeners); err != nil {
-		if !errors.Is(err, fs.ErrNotExist) {
-			return err
-		}
+		return err
 	}
 
 	return nil
diff --git a/internal/nonkube/client/fs/router_access_handler.go b/internal/nonkube/client/fs/router_access_handler.go
index 59260d8b..f1ab2d26 100644
--- a/internal/nonkube/client/fs/router_access_handler.go
+++ b/internal/nonkube/client/fs/router_access_handler.go
@@ -1,10 +1,7 @@
 package fs
 
 import (
-	"errors"
 	"fmt"
-	"io/fs"
-
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
 )
@@ -64,10 +61,7 @@ func (s *RouterAccessHandler) Delete(name string) error {
 	fileName := name + ".yaml"
 
 	if err := s.DeleteFile(s.pathProvider.GetNamespace(), fileName, common.RouterAccesses); err != nil {
-		fmt.Println(err)
-		if !errors.Is(err, fs.ErrNotExist) {
-			return err
-		}
+		return err
 	}
 
 	return nil
@@ -77,7 +71,7 @@ func (s *RouterAccessHandler) Update(name string) (*v2alpha1.RouterAccess, error
 	var context v2alpha1.RouterAccess
 	fileName := name + ".yaml"
 
-	// read from input directory to get lastest config
+	// read from input directory to get latest config
 	err, file := s.ReadFile(s.pathProvider.GetNamespace(), fileName, common.RouterAccesses)
 	if err != nil {
 		return nil, err
diff --git a/internal/nonkube/client/fs/secret_handler.go b/internal/nonkube/client/fs/secret_handler.go
index f897a1ed..ef8c7a13 100644
--- a/internal/nonkube/client/fs/secret_handler.go
+++ b/internal/nonkube/client/fs/secret_handler.go
@@ -1,9 +1,7 @@
 package fs
 
 import (
-	"errors"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
-	"io/fs"
 	v1 "k8s.io/api/core/v1"
 )
 
@@ -42,9 +40,7 @@ func (s *SecretHandler) Delete(name string) error {
 	fileName := name + ".yaml"
 
 	if err := s.DeleteFile(s.pathProvider.GetNamespace(), fileName, common.Secrets); err != nil {
-		if !errors.Is(err, fs.ErrNotExist) {
-			return err
-		}
+		return err
 	}
 
 	return nil
diff --git a/internal/nonkube/client/fs/secured_access_handler.go b/internal/nonkube/client/fs/secured_access_handler.go
new file mode 100644
index 00000000..c74b2b22
--- /dev/null
+++ b/internal/nonkube/client/fs/secured_access_handler.go
@@ -0,0 +1,51 @@
+package fs
+
+import (
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
+)
+
+type SecuredAccessHandler struct {
+	BaseCustomResourceHandler
+	pathProvider PathProvider
+}
+
+func NewSecuredAccessHandler(namespace string) *SecuredAccessHandler {
+	return &SecuredAccessHandler{
+		pathProvider: PathProvider{
+			Namespace: namespace,
+		},
+	}
+}
+
+func (s *SecuredAccessHandler) Add(resource v2alpha1.SecuredAccess) error {
+
+	fileName := resource.Name + ".yaml"
+	content, err := s.EncodeToYaml(resource)
+	if err != nil {
+		return err
+	}
+
+	err = s.WriteFile(s.pathProvider.GetNamespace(), fileName, content, common.SecuredAccesses)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (s *SecuredAccessHandler) Get(name string, opt GetOptions) (*v2alpha1.SecuredAccess, error) {
+	return nil, nil
+}
+
+func (s *SecuredAccessHandler) Delete(name string) error {
+	fileName := name + ".yaml"
+
+	if err := s.DeleteFile(s.pathProvider.GetNamespace(), fileName, common.SecuredAccesses); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (s *SecuredAccessHandler) List() ([]*v2alpha1.SecuredAccess, error) { return nil, nil }
diff --git a/internal/nonkube/client/fs/site_handler.go b/internal/nonkube/client/fs/site_handler.go
index 50ac7a62..42ceb264 100644
--- a/internal/nonkube/client/fs/site_handler.go
+++ b/internal/nonkube/client/fs/site_handler.go
@@ -60,7 +60,7 @@ func (s *SiteHandler) Get(name string, opts GetOptions) (*v2alpha1.Site, error)
 			return nil, err
 		}
 	} else {
-		// read from input directory to get lastest config
+		// read from input directory to get latest config
 		err, file := s.ReadFile(s.pathProvider.GetNamespace(), fileName, common.Sites)
 		if err != nil {
 			return nil, err
@@ -77,9 +77,7 @@ func (s *SiteHandler) Delete(name string) error {
 	if name != "" {
 		fileName := name + ".yaml"
 		if err := s.DeleteFile(s.pathProvider.GetNamespace(), fileName, common.Sites); err != nil {
-			if !errors.Is(err, fs.ErrNotExist) {
-				return err
-			}
+			return err
 		}
 	} else {
 		// remove directory and its contents
diff --git a/internal/nonkube/common/claim_redeemer.go b/internal/nonkube/common/claim_redeemer.go
index 5bd777b7..56bbaf6b 100644
--- a/internal/nonkube/common/claim_redeemer.go
+++ b/internal/nonkube/common/claim_redeemer.go
@@ -83,7 +83,7 @@ func RedeemAccessToken(claim *skupperv2alpha1.AccessToken, subject string) (*Lin
 		return nil, err
 	}
 	// TODO should bootstrap log helpful status info (like the following)?
-	// log.Printf("HTTP Post to %s for %s/%s was sucessful, decoding response body", claim.Spec.Url, claim.Namespace, claim.Name)
+	// log.Printf("HTTP Post to %s for %s/%s was successful, decoding response body", claim.Spec.Url, claim.Namespace, claim.Name)
 
 	decoder := newLinkDecoder(response.Body)
 	if err := decoder.decodeAll(); err != nil {
diff --git a/pkg/vanflow/encoding/encoding.go b/pkg/vanflow/encoding/encoding.go
index 24ee817b..3b9a63b8 100644
--- a/pkg/vanflow/encoding/encoding.go
+++ b/pkg/vanflow/encoding/encoding.go
@@ -1,4 +1,4 @@
-// encoding exposes functionality to convert between an arbitray maps and
+// encoding exposes functionality to convert between an arbitrary maps and
 // native Go structs in service of the Vanflow protocol.
 //
 // The Vanflow protocol uses the amqp-value section to represent record types
diff --git a/pkg/vanflow/eventsource/doc.go b/pkg/vanflow/eventsource/doc.go
index 18b66ace..3113884c 100644
--- a/pkg/vanflow/eventsource/doc.go
+++ b/pkg/vanflow/eventsource/doc.go
@@ -1,5 +1,5 @@
 /*
-The eventsource package contains mechanisims for discovering and interracting
+The eventsource package contains mechanisims for discovering and interacting
 with vanflow event sources.
 */
 package eventsource
diff --git a/pkg/vanflow/session/container.go b/pkg/vanflow/session/container.go
index 154bb0b7..fbff2452 100644
--- a/pkg/vanflow/session/container.go
+++ b/pkg/vanflow/session/container.go
@@ -1,5 +1,5 @@
 // session implements amqp connection and session management though the concept
-// of a Container, inspired by the Conatiner interface exposed by the qpid
+// of a Container, inspired by the Container interface exposed by the qpid
 // proton amqp libraries. This abstraction allows vanflow components to be
 // written without repeating connection management tasks.
 package session
@@ -39,7 +39,7 @@ func (o SenderOptions) get() amqp.SenderOptions {
 type Container interface {
 	// Start the container
 	Start(context.Context)
-	// NewReceiver adds a new reciver link using the container's session
+	// NewReceiver adds a new receiver link using the container's session
 	NewReceiver(address string, opts ReceiverOptions) Receiver
 	// NewSender adds a new sender link using the container's session
 	NewSender(address string, opts SenderOptions) Sender
@@ -92,7 +92,7 @@ func (cfg ContainerConfig) toAmqp() *amqp.ConnOptions {
 	return &opts
 }
 
-// NewContainer creats an amqp container that will attempt to create a single
+// NewContainer creates an amqp container that will attempt to create a single
 // connection + session pair using the supplied amqp connection options for use
 // with the container's Senders and Receivers. Will recreate the connection and
 // session when a link encounters an error using the specified backoff
@@ -221,7 +221,7 @@ func (c *container) Start(ctx context.Context) {
 			if errors.Is(err, ctx.Err()) {
 				return
 			}
-			wErr := fmt.Errorf("error caused contianer to close: %w", err)
+			wErr := fmt.Errorf("error caused container to close: %w", err)
 			c.mu.Lock()
 			defer c.mu.Unlock()
 			for _, handler := range c.errorHandlers {
diff --git a/pkg/vanflow/store/syncmap.go b/pkg/vanflow/store/syncmap.go
index 9e78f7c5..5248e917 100644
--- a/pkg/vanflow/store/syncmap.go
+++ b/pkg/vanflow/store/syncmap.go
@@ -20,7 +20,7 @@ type syncMapStore struct {
 	items map[string]Entry
 
 	indexers      map[string]Indexer
-	indicies      map[string]map[string]keySet
+	indices       map[string]map[string]keySet
 	eventHandlers EventHandlerFuncs
 }
 
@@ -37,8 +37,8 @@ func NewSyncMapStore(cfg SyncMapStoreConfig) Interface {
 		indexers:      cfg.Indexers,
 		eventHandlers: cfg.Handlers,
 
-		items:    make(map[string]Entry),
-		indicies: make(map[string]map[string]keySet),
+		items:   make(map[string]Entry),
+		indices: make(map[string]map[string]keySet),
 	}
 }
 
@@ -201,7 +201,7 @@ func (m *syncMapStore) Index(index string, exemplar Entry) []Entry {
 	if !ok {
 		return nil
 	}
-	idx := m.indicies[index]
+	idx := m.indices[index]
 	indexVals := indexer(exemplar)
 
 	keys := make(keySet)
@@ -220,7 +220,7 @@ func (m *syncMapStore) IndexValues(index string) []string {
 	m.mu.RLock()
 	defer m.mu.RUnlock()
 
-	idx := m.indicies[index]
+	idx := m.indices[index]
 	if len(idx) == 0 {
 		return nil
 	}
@@ -241,7 +241,7 @@ func (m *syncMapStore) Replace(items []Entry) {
 	}
 	m.items = entries
 
-	m.indicies = make(map[string]map[string]keySet)
+	m.indices = make(map[string]map[string]keySet)
 	for key, entry := range m.items {
 		m.reindex(key, nil, entry)
 	}
@@ -251,7 +251,7 @@ func (m *syncMapStore) unindex(key string, entry Entry) {
 	for name, indexer := range m.indexers {
 		indexVals := indexer(entry)
 
-		index := m.indicies[name]
+		index := m.indices[name]
 		if index == nil {
 			continue
 		}
@@ -273,10 +273,10 @@ func (m *syncMapStore) reindex(key string, prev *Entry, next Entry) {
 	for name, indexer := range m.indexers {
 		indexVals := indexer(next)
 
-		index := m.indicies[name]
+		index := m.indices[name]
 		if index == nil {
 			index = map[string]keySet{}
-			m.indicies[name] = index
+			m.indices[name] = index
 		}
 		for _, indexVal := range indexVals {
 			set := index[indexVal]


Test patch:
diff --git a/cmd/network-observer/internal/cmd/commands_test.go b/cmd/network-observer/internal/cmd/commands_test.go
index ff8a0fc9..ebb5e29c 100644
--- a/cmd/network-observer/internal/cmd/commands_test.go
+++ b/cmd/network-observer/internal/cmd/commands_test.go
@@ -127,7 +127,7 @@ func TestEnsureSecrets(t *testing.T) {
 			case err != nil && tc.ExpectErr:
 				// OKAY
 			case err != nil && !tc.ExpectErr:
-				t.Fatalf("Unxpected error %s", err)
+				t.Fatalf("Unexpected error %s", err)
 			}
 
 			if tc.Assert != nil {
diff --git a/cmd/network-observer/internal/flowlog/logging_test.go b/cmd/network-observer/internal/flowlog/logging_test.go
index ef898295..b850c68e 100644
--- a/cmd/network-observer/internal/flowlog/logging_test.go
+++ b/cmd/network-observer/internal/flowlog/logging_test.go
@@ -47,7 +47,7 @@ func TestSampling(t *testing.T) {
 		assert.Equal(t, ct, 2_000)
 	})
 
-	t.Run("rate limt zero with burst", func(t *testing.T) {
+	t.Run("rate limit zero with burst", func(t *testing.T) {
 		ct := 0
 		messageCounter := func(string, ...any) {
 			ct++
diff --git a/internal/cmd/skupper/connector/nonkube/connector_create_test.go b/internal/cmd/skupper/connector/nonkube/connector_create_test.go
index 50f086ec..eab9bed0 100644
--- a/internal/cmd/skupper/connector/nonkube/connector_create_test.go
+++ b/internal/cmd/skupper/connector/nonkube/connector_create_test.go
@@ -91,7 +91,7 @@ func TestNonKubeCmdConnectorCreate_ValidateInput(t *testing.T) {
 			expectedError: "host is not valid: a valid IP address or hostname is expected",
 		},
 		{
-			name:  "host is not configued default",
+			name:  "host is not configured default",
 			args:  []string{"my-connector-host", "8080"},
 			flags: &common.CommandConnectorCreateFlags{},
 		},
diff --git a/internal/cmd/skupper/connector/nonkube/connector_delete_test.go b/internal/cmd/skupper/connector/nonkube/connector_delete_test.go
index 1e0f4ed8..bbaf28d4 100644
--- a/internal/cmd/skupper/connector/nonkube/connector_delete_test.go
+++ b/internal/cmd/skupper/connector/nonkube/connector_delete_test.go
@@ -3,6 +3,7 @@ package nonkube
 import (
 	"os"
 	"path/filepath"
+	"strings"
 	"testing"
 
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
@@ -13,7 +14,6 @@ import (
 	"github.com/spf13/cobra"
 	"gotest.tools/v3/assert"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime"
 )
 
 func TestCmdConnectorDelete_ValidateInput(t *testing.T) {
@@ -22,14 +22,16 @@ func TestCmdConnectorDelete_ValidateInput(t *testing.T) {
 		args              []string
 		flags             *common.CommandConnectorDeleteFlags
 		cobraGenericFlags map[string]string
-		k8sObjects        []runtime.Object
-		skupperObjects    []runtime.Object
 		expectedError     string
 	}
 
-	homeDir, err := os.UserHomeDir()
-	assert.Check(t, err == nil)
-	path := filepath.Join(homeDir, "/.local/share/skupper/namespaces/test/", string(api.InputSiteStatePath))
+	if os.Getuid() == 0 {
+		api.DefaultRootDataHome = t.TempDir()
+	} else {
+		t.Setenv("XDG_DATA_HOME", t.TempDir())
+	}
+	tmpDir := api.GetDataHome()
+	path := filepath.Join(tmpDir, "/namespaces/test/", string(api.InputSiteStatePath))
 
 	testTable := []test{
 		{
@@ -117,28 +119,30 @@ func TestCmdConnectorDelete_ValidateInput(t *testing.T) {
 
 func TestCmdConnectorDelete_Run(t *testing.T) {
 	type test struct {
-		name                string
-		namespace           string
-		deleteName          string
-		k8sObjects          []runtime.Object
-		skupperObjects      []runtime.Object
-		skupperErrorMessage string
-		errorMessage        string
+		name         string
+		namespace    string
+		deleteName   string
+		errorMessage string
+	}
+
+	if os.Getuid() == 0 {
+		api.DefaultRootDataHome = t.TempDir()
+	} else {
+		t.Setenv("XDG_DATA_HOME", t.TempDir())
 	}
+	tmpDir := api.GetDataHome()
+	path := filepath.Join(tmpDir, "/namespaces/test/", string(api.InputSiteStatePath))
 
 	testTable := []test{
 		{
-			name:                "run fails default",
-			deleteName:          "my-connector",
-			skupperErrorMessage: "error",
-			errorMessage:        "error",
+			name:         "run fails default",
+			deleteName:   "my-connector",
+			errorMessage: "no such file or directory",
 		},
 		{
-			name:                "run fails",
-			namespace:           "test",
-			deleteName:          "my-connector",
-			skupperErrorMessage: "error",
-			errorMessage:        "error",
+			name:       "runs ok",
+			namespace:  "test",
+			deleteName: "my-connector",
 		},
 	}
 
@@ -147,17 +151,39 @@ func TestCmdConnectorDelete_Run(t *testing.T) {
 
 		t.Run(test.name, func(t *testing.T) {
 
+			createConnectorResource(path, t)
 			cmd.connectorName = test.deleteName
 			cmd.namespace = test.namespace
 			cmd.connectorHandler = fs.NewConnectorHandler(cmd.namespace)
 			cmd.InputToOptions()
 
 			err := cmd.Run()
-			if err != nil {
-				assert.Check(t, test.errorMessage == err.Error())
+
+			if test.errorMessage != "" {
+				assert.Check(t, strings.HasSuffix(err.Error(), test.errorMessage), err.Error())
 			} else {
 				assert.Check(t, err == nil)
 			}
 		})
 	}
 }
+
+func createConnectorResource(path string, t *testing.T) {
+	connectorResource := v2alpha1.Connector{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "Connector",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "my-connector",
+			Namespace: "test",
+		},
+	}
+
+	connectorHandler := fs.NewConnectorHandler("test")
+
+	contentConnector, err := connectorHandler.EncodeToYaml(connectorResource)
+	assert.Check(t, err == nil)
+	err = connectorHandler.WriteFile(path, "my-connector.yaml", contentConnector, common.Connectors)
+	assert.Check(t, err == nil)
+}
diff --git a/internal/cmd/skupper/connector/nonkube/connector_generate_test.go b/internal/cmd/skupper/connector/nonkube/connector_generate_test.go
index 7442889e..a036bcec 100644
--- a/internal/cmd/skupper/connector/nonkube/connector_generate_test.go
+++ b/internal/cmd/skupper/connector/nonkube/connector_generate_test.go
@@ -85,7 +85,7 @@ func TestNonKubeCmdConnectorGenerate_ValidateInput(t *testing.T) {
 			expectedError: "host is not valid: a valid IP address or hostname is expected",
 		},
 		{
-			name:  "host is not configued default",
+			name:  "host is not configured default",
 			args:  []string{"my-connector-host", "8080"},
 			flags: &common.CommandConnectorGenerateFlags{},
 		},
diff --git a/internal/cmd/skupper/connector/nonkube/connector_status_test.go b/internal/cmd/skupper/connector/nonkube/connector_status_test.go
index 48d7e969..340b6df0 100644
--- a/internal/cmd/skupper/connector/nonkube/connector_status_test.go
+++ b/internal/cmd/skupper/connector/nonkube/connector_status_test.go
@@ -3,6 +3,7 @@ package nonkube
 import (
 	"os"
 	"path/filepath"
+	"strings"
 	"testing"
 
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
@@ -12,7 +13,6 @@ import (
 	"github.com/skupperproject/skupper/pkg/nonkube/api"
 	"gotest.tools/v3/assert"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime"
 )
 
 func TestCmdConnectorStatus_ValidateInput(t *testing.T) {
@@ -21,14 +21,16 @@ func TestCmdConnectorStatus_ValidateInput(t *testing.T) {
 		args              []string
 		flags             *common.CommandConnectorStatusFlags
 		cobraGenericFlags map[string]string
-		k8sObjects        []runtime.Object
-		skupperObjects    []runtime.Object
 		expectedError     string
 	}
 
-	homeDir, err := os.UserHomeDir()
-	assert.Check(t, err == nil)
-	path := filepath.Join(homeDir, "/.local/share/skupper/namespaces/test/", string(api.RuntimeSiteStatePath))
+	if os.Getuid() == 0 {
+		api.DefaultRootDataHome = t.TempDir()
+	} else {
+		t.Setenv("XDG_DATA_HOME", t.TempDir())
+	}
+	tmpDir := api.GetDataHome()
+	path := filepath.Join(tmpDir, "/namespaces/test/", string(api.RuntimeSiteStatePath))
 
 	testTable := []test{
 		{
@@ -117,24 +119,25 @@ func TestCmdConnectorStatus_ValidateInput(t *testing.T) {
 
 func TestCmdConnectorStatus_Run(t *testing.T) {
 	type test struct {
-		name                string
-		connectorName       string
-		flags               common.CommandConnectorStatusFlags
-		k8sObjects          []runtime.Object
-		skupperObjects      []runtime.Object
-		skupperErrorMessage string
-		errorMessage        string
+		name          string
+		connectorName string
+		flags         common.CommandConnectorStatusFlags
+		errorMessage  string
 	}
 
-	homeDir, err := os.UserHomeDir()
-	assert.Check(t, err == nil)
-	path := filepath.Join(homeDir, "/.local/share/skupper/namespaces/test/", string(api.InputSiteStatePath))
+	if os.Getuid() == 0 {
+		api.DefaultRootDataHome = t.TempDir()
+	} else {
+		t.Setenv("XDG_DATA_HOME", t.TempDir())
+	}
+	tmpDir := api.GetDataHome()
+	path := filepath.Join(tmpDir, "/namespaces/test/", string(api.RuntimeSiteStatePath))
 
 	testTable := []test{
 		{
 			name:          "run fails connector doesn't exist",
 			connectorName: "no-connector",
-			errorMessage:  "failed to read file: open " + path + "/Connector-no-connector.yaml: no such file or directory",
+			errorMessage:  "no such file or directory",
 		},
 		{
 			name:          "runs ok, returns 1 connectors",
@@ -225,7 +228,6 @@ func TestCmdConnectorStatus_Run(t *testing.T) {
 	defer command.connectorHandler.Delete("my-connector")
 	defer command.connectorHandler.Delete("my-connector2")
 
-	path = filepath.Join(homeDir, "/.local/share/skupper/namespaces/test/", string(api.RuntimeSiteStatePath))
 	content, err := command.connectorHandler.EncodeToYaml(connectorResource1)
 	assert.Check(t, err == nil)
 	err = command.connectorHandler.WriteFile(path, "my-connector.yaml", content, common.Connectors)
@@ -244,7 +246,7 @@ func TestCmdConnectorStatus_Run(t *testing.T) {
 		t.Run(test.name, func(t *testing.T) {
 			err := command.Run()
 			if err != nil {
-				assert.Equal(t, test.errorMessage, err.Error())
+				assert.Check(t, strings.HasSuffix(err.Error(), test.errorMessage))
 			} else {
 				assert.NilError(t, err)
 			}
@@ -254,22 +256,21 @@ func TestCmdConnectorStatus_Run(t *testing.T) {
 
 func TestCmdConnectorStatus_RunNoDirectory(t *testing.T) {
 	type test struct {
-		name                string
-		flags               common.CommandConnectorStatusFlags
-		k8sObjects          []runtime.Object
-		skupperObjects      []runtime.Object
-		skupperErrorMessage string
-		errorMessage        string
+		name         string
+		flags        common.CommandConnectorStatusFlags
+		errorMessage string
 	}
 
-	homeDir, err := os.UserHomeDir()
-	assert.Check(t, err == nil)
-	path := filepath.Join(homeDir, "/.local/share/skupper/namespaces/test1/", string(api.InputSiteStatePath))
+	if os.Getuid() == 0 {
+		api.DefaultRootDataHome = t.TempDir()
+	} else {
+		t.Setenv("XDG_DATA_HOME", t.TempDir())
+	}
 
 	testTable := []test{
 		{
-			name:         "runs fails no directory",
-			errorMessage: "failed to read file: open " + path + "/Connector-my-connector.yaml: no such file or directory",
+			name:         "run function fails because the file does not exist",
+			errorMessage: "no such file or directory",
 		},
 	}
 
@@ -284,7 +285,7 @@ func TestCmdConnectorStatus_RunNoDirectory(t *testing.T) {
 
 			err := command.Run()
 			if err != nil {
-				assert.Check(t, test.errorMessage == err.Error())
+				assert.Check(t, strings.HasSuffix(err.Error(), test.errorMessage))
 			} else {
 				assert.Check(t, err == nil)
 			}
diff --git a/internal/cmd/skupper/connector/nonkube/connector_update_test.go b/internal/cmd/skupper/connector/nonkube/connector_update_test.go
index 558bba28..2f095b37 100644
--- a/internal/cmd/skupper/connector/nonkube/connector_update_test.go
+++ b/internal/cmd/skupper/connector/nonkube/connector_update_test.go
@@ -13,7 +13,6 @@ import (
 	"github.com/spf13/cobra"
 	"gotest.tools/v3/assert"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime"
 )
 
 func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
@@ -21,15 +20,18 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 		name              string
 		args              []string
 		flags             *common.CommandConnectorUpdateFlags
-		k8sObjects        []runtime.Object
-		skupperObjects    []runtime.Object
 		cobraGenericFlags map[string]string
 		expectedError     string
 	}
 
-	homeDir, err := os.UserHomeDir()
-	assert.Check(t, err == nil)
-	path := filepath.Join(homeDir, "/.local/share/skupper/namespaces/test/", string(api.InputSiteStatePath))
+	if os.Getuid() == 0 {
+		api.DefaultRootDataHome = t.TempDir()
+	} else {
+		t.Setenv("XDG_DATA_HOME", t.TempDir())
+	}
+	tmpDir := api.GetDataHome()
+	path := filepath.Join(tmpDir, "/namespaces/test/", string(api.InputSiteStatePath))
+
 	testTable := []test{
 		{
 			name:          "connector is not updated because get connector returned error",
@@ -163,18 +165,15 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 
 func TestCmdConnectorUpdate_Run(t *testing.T) {
 	type test struct {
-		name                string
-		namespace           string
-		k8sObjects          []runtime.Object
-		skupperObjects      []runtime.Object
-		skupperErrorMessage string
-		errorMessage        string
-		connectorName       string
-		host                string
-		routingKey          string
-		tlsCredentials      string
-		connectorType       string
-		port                int
+		name           string
+		namespace      string
+		errorMessage   string
+		connectorName  string
+		host           string
+		routingKey     string
+		tlsCredentials string
+		connectorType  string
+		port           int
 	}
 
 	testTable := []test{
diff --git a/internal/cmd/skupper/listener/nonkube/listener_delete_test.go b/internal/cmd/skupper/listener/nonkube/listener_delete_test.go
index ea9baece..9a8e97b4 100644
--- a/internal/cmd/skupper/listener/nonkube/listener_delete_test.go
+++ b/internal/cmd/skupper/listener/nonkube/listener_delete_test.go
@@ -3,6 +3,7 @@ package nonkube
 import (
 	"os"
 	"path/filepath"
+	"strings"
 	"testing"
 
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
@@ -13,7 +14,6 @@ import (
 	"github.com/spf13/cobra"
 	"gotest.tools/v3/assert"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime"
 )
 
 func TestCmdListenerDelete_ValidateInput(t *testing.T) {
@@ -22,14 +22,16 @@ func TestCmdListenerDelete_ValidateInput(t *testing.T) {
 		args              []string
 		flags             *common.CommandListenerDeleteFlags
 		cobraGenericFlags map[string]string
-		k8sObjects        []runtime.Object
-		skupperObjects    []runtime.Object
 		expectedError     string
 	}
 
-	homeDir, err := os.UserHomeDir()
-	assert.Check(t, err == nil)
-	path := filepath.Join(homeDir, "/.local/share/skupper/namespaces/test/", string(api.InputSiteStatePath))
+	if os.Getuid() == 0 {
+		api.DefaultRootDataHome = t.TempDir()
+	} else {
+		t.Setenv("XDG_DATA_HOME", t.TempDir())
+	}
+	tmpDir := api.GetDataHome()
+	path := filepath.Join(tmpDir, "/namespaces/test/", string(api.InputSiteStatePath))
 
 	testTable := []test{
 		{
@@ -117,28 +119,30 @@ func TestCmdListenerDelete_ValidateInput(t *testing.T) {
 
 func TestCmdListenerDelete_Run(t *testing.T) {
 	type test struct {
-		name                string
-		namespace           string
-		deleteName          string
-		k8sObjects          []runtime.Object
-		skupperObjects      []runtime.Object
-		skupperErrorMessage string
-		errorMessage        string
+		name         string
+		namespace    string
+		deleteName   string
+		errorMessage string
 	}
 
+	if os.Getuid() == 0 {
+		api.DefaultRootDataHome = t.TempDir()
+	} else {
+		t.Setenv("XDG_DATA_HOME", t.TempDir())
+	}
+	tmpDir := api.GetDataHome()
+	path := filepath.Join(tmpDir, "/namespaces/test/", string(api.InputSiteStatePath))
+
 	testTable := []test{
 		{
-			name:                "run fails default",
-			deleteName:          "my-listener",
-			skupperErrorMessage: "error",
-			errorMessage:        "error",
+			name:         "run fails default",
+			deleteName:   "my-listener",
+			errorMessage: "no such file or directory",
 		},
 		{
-			name:                "run fails",
-			namespace:           "test",
-			deleteName:          "my-listener",
-			skupperErrorMessage: "error",
-			errorMessage:        "error",
+			name:       "run ok",
+			namespace:  "test",
+			deleteName: "my-listener",
 		},
 	}
 
@@ -147,17 +151,38 @@ func TestCmdListenerDelete_Run(t *testing.T) {
 
 		t.Run(test.name, func(t *testing.T) {
 
+			createListenerResource(path, t)
 			cmd.listenerName = test.deleteName
 			cmd.namespace = test.namespace
 			cmd.listenerHandler = fs.NewListenerHandler(cmd.namespace)
 			cmd.InputToOptions()
 
 			err := cmd.Run()
-			if err != nil {
-				assert.Check(t, test.errorMessage == err.Error())
+			if test.errorMessage != "" {
+				assert.Check(t, strings.HasSuffix(err.Error(), test.errorMessage))
 			} else {
 				assert.Check(t, err == nil)
 			}
 		})
 	}
 }
+
+func createListenerResource(path string, t *testing.T) {
+	listenerResource := v2alpha1.Listener{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "Listener",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "my-listener",
+			Namespace: "test",
+		},
+	}
+
+	listenerHandler := fs.NewListenerHandler("test")
+
+	contentConnector, err := listenerHandler.EncodeToYaml(listenerResource)
+	assert.Check(t, err == nil)
+	err = listenerHandler.WriteFile(path, "my-listener.yaml", contentConnector, common.Listeners)
+	assert.Check(t, err == nil)
+}
diff --git a/internal/cmd/skupper/listener/nonkube/listener_status_test.go b/internal/cmd/skupper/listener/nonkube/listener_status_test.go
index 13cc50b6..b7a0d850 100644
--- a/internal/cmd/skupper/listener/nonkube/listener_status_test.go
+++ b/internal/cmd/skupper/listener/nonkube/listener_status_test.go
@@ -3,6 +3,7 @@ package nonkube
 import (
 	"os"
 	"path/filepath"
+	"strings"
 	"testing"
 
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
@@ -12,7 +13,6 @@ import (
 	"github.com/skupperproject/skupper/pkg/nonkube/api"
 	"gotest.tools/v3/assert"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime"
 )
 
 func TestCmdListenerStatus_ValidateInput(t *testing.T) {
@@ -21,14 +21,16 @@ func TestCmdListenerStatus_ValidateInput(t *testing.T) {
 		args              []string
 		flags             *common.CommandListenerStatusFlags
 		cobraGenericFlags map[string]string
-		k8sObjects        []runtime.Object
-		skupperObjects    []runtime.Object
 		expectedError     string
 	}
 
-	homeDir, err := os.UserHomeDir()
-	assert.Check(t, err == nil)
-	path := filepath.Join(homeDir, "/.local/share/skupper/namespaces/test/", string(api.RuntimeSiteStatePath))
+	if os.Getuid() == 0 {
+		api.DefaultRootDataHome = t.TempDir()
+	} else {
+		t.Setenv("XDG_DATA_HOME", t.TempDir())
+	}
+	tmpDir := api.GetDataHome()
+	path := filepath.Join(tmpDir, "/namespaces/test/", string(api.RuntimeSiteStatePath))
 
 	testTable := []test{
 		{
@@ -117,24 +119,25 @@ func TestCmdListenerStatus_ValidateInput(t *testing.T) {
 
 func TestCmdListenerStatus_Run(t *testing.T) {
 	type test struct {
-		name                string
-		listenerName        string
-		flags               common.CommandListenerStatusFlags
-		k8sObjects          []runtime.Object
-		skupperObjects      []runtime.Object
-		skupperErrorMessage string
-		errorMessage        string
+		name         string
+		listenerName string
+		flags        common.CommandListenerStatusFlags
+		errorMessage string
 	}
 
-	homeDir, err := os.UserHomeDir()
-	assert.Check(t, err == nil)
-	path := filepath.Join(homeDir, "/.local/share/skupper/namespaces/test/", string(api.InputSiteStatePath))
+	if os.Getuid() == 0 {
+		api.DefaultRootDataHome = t.TempDir()
+	} else {
+		t.Setenv("XDG_DATA_HOME", t.TempDir())
+	}
+	tmpDir := api.GetDataHome()
+	path := filepath.Join(tmpDir, "/namespaces/test/", string(api.RuntimeSiteStatePath))
 
 	testTable := []test{
 		{
 			name:         "run fails listener doesn't exist",
 			listenerName: "no-listener",
-			errorMessage: "failed to read file: open " + path + "/Listener-no-listener.yaml: no such file or directory",
+			errorMessage: "no such file or directory",
 		},
 		{
 			name:         "runs ok, returns 1 listeners",
@@ -225,7 +228,6 @@ func TestCmdListenerStatus_Run(t *testing.T) {
 	defer command.listenerHandler.Delete("my-listener")
 	defer command.listenerHandler.Delete("my-listener2")
 
-	path = filepath.Join(homeDir, "/.local/share/skupper/namespaces/test/", string(api.RuntimeSiteStatePath))
 	content, err := command.listenerHandler.EncodeToYaml(listenerResource1)
 	assert.Check(t, err == nil)
 	err = command.listenerHandler.WriteFile(path, "my-listener.yaml", content, common.Listeners)
@@ -244,7 +246,7 @@ func TestCmdListenerStatus_Run(t *testing.T) {
 		t.Run(test.name, func(t *testing.T) {
 			err := command.Run()
 			if err != nil {
-				assert.Check(t, test.errorMessage == err.Error())
+				assert.Check(t, strings.HasSuffix(err.Error(), test.errorMessage))
 			} else {
 				assert.Check(t, err == nil)
 			}
@@ -254,22 +256,21 @@ func TestCmdListenerStatus_Run(t *testing.T) {
 
 func TestCmdListenerStatus_RunNoDirectory(t *testing.T) {
 	type test struct {
-		name                string
-		flags               common.CommandListenerStatusFlags
-		k8sObjects          []runtime.Object
-		skupperObjects      []runtime.Object
-		skupperErrorMessage string
-		errorMessage        string
+		name         string
+		flags        common.CommandListenerStatusFlags
+		errorMessage string
 	}
 
-	homeDir, err := os.UserHomeDir()
-	assert.Check(t, err == nil)
-	path := filepath.Join(homeDir, "/.local/share/skupper/namespaces/test1/", string(api.InputSiteStatePath))
+	if os.Getuid() == 0 {
+		api.DefaultRootDataHome = t.TempDir()
+	} else {
+		t.Setenv("XDG_DATA_HOME", t.TempDir())
+	}
 
 	testTable := []test{
 		{
 			name:         "runs fails no directory",
-			errorMessage: "failed to read file: open " + path + "/Listener-my-listener.yaml: no such file or directory",
+			errorMessage: "no such file or directory",
 		},
 	}
 
@@ -284,7 +285,7 @@ func TestCmdListenerStatus_RunNoDirectory(t *testing.T) {
 
 			err := command.Run()
 			if err != nil {
-				assert.Check(t, test.errorMessage == err.Error())
+				assert.Check(t, strings.HasSuffix(err.Error(), test.errorMessage))
 			} else {
 				assert.Check(t, err == nil)
 			}
diff --git a/internal/cmd/skupper/listener/nonkube/listener_update_test.go b/internal/cmd/skupper/listener/nonkube/listener_update_test.go
index 145dd758..32681bd0 100644
--- a/internal/cmd/skupper/listener/nonkube/listener_update_test.go
+++ b/internal/cmd/skupper/listener/nonkube/listener_update_test.go
@@ -21,15 +21,17 @@ func TestCmdListenerUpdate_ValidateInput(t *testing.T) {
 		name              string
 		args              []string
 		flags             *common.CommandListenerUpdateFlags
-		k8sObjects        []runtime.Object
-		skupperObjects    []runtime.Object
 		cobraGenericFlags map[string]string
 		expectedError     string
 	}
 
-	homeDir, err := os.UserHomeDir()
-	assert.Check(t, err == nil)
-	path := filepath.Join(homeDir, "/.local/share/skupper/namespaces/test/", string(api.InputSiteStatePath))
+	if os.Getuid() == 0 {
+		api.DefaultRootDataHome = t.TempDir()
+	} else {
+		t.Setenv("XDG_DATA_HOME", t.TempDir())
+	}
+	tmpDir := api.GetDataHome()
+	path := filepath.Join(tmpDir, "/namespaces/test/", string(api.InputSiteStatePath))
 
 	testTable := []test{
 		{
diff --git a/internal/cmd/skupper/site/nonkube/site_create_test.go b/internal/cmd/skupper/site/nonkube/site_create_test.go
index 26171642..332d7e7c 100644
--- a/internal/cmd/skupper/site/nonkube/site_create_test.go
+++ b/internal/cmd/skupper/site/nonkube/site_create_test.go
@@ -8,7 +8,6 @@ import (
 	"github.com/skupperproject/skupper/internal/nonkube/client/fs"
 	"github.com/spf13/cobra"
 	"gotest.tools/v3/assert"
-	"k8s.io/apimachinery/pkg/runtime"
 )
 
 func TestNonKubeCmdSiteCreate_ValidateInput(t *testing.T) {
@@ -136,9 +135,6 @@ func TestNonKubeCmdSiteCreate_InputToOptions(t *testing.T) {
 func TestNonKubeCmdSiteCreate_Run(t *testing.T) {
 	type test struct {
 		name              string
-		k8sObjects        []runtime.Object
-		skupperObjects    []runtime.Object
-		skupperError      string
 		siteName          string
 		errorMessage      string
 		routerAccessName  string
@@ -148,18 +144,13 @@ func TestNonKubeCmdSiteCreate_Run(t *testing.T) {
 	testTable := []test{
 		{
 			name:              "runs ok",
-			k8sObjects:        nil,
-			skupperObjects:    nil,
 			siteName:          "my-site",
 			routerAccessName:  "ra-test",
 			linkAccessEnabled: true,
 		},
 		{
-			name:           "runs ok without creating site",
-			k8sObjects:     nil,
-			skupperObjects: nil,
-			siteName:       "test",
-			skupperError:   "",
+			name:     "runs ok without create site",
+			siteName: "test",
 		},
 	}
 
diff --git a/internal/cmd/skupper/site/nonkube/site_delete_test.go b/internal/cmd/skupper/site/nonkube/site_delete_test.go
index 560d7e0f..57631530 100644
--- a/internal/cmd/skupper/site/nonkube/site_delete_test.go
+++ b/internal/cmd/skupper/site/nonkube/site_delete_test.go
@@ -3,6 +3,7 @@ package nonkube
 import (
 	"os"
 	"path/filepath"
+	"strings"
 	"testing"
 
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
@@ -13,7 +14,6 @@ import (
 	"github.com/spf13/cobra"
 	"gotest.tools/v3/assert"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime"
 )
 
 func TestCmdSiteDelete_ValidateInput(t *testing.T) {
@@ -22,8 +22,6 @@ func TestCmdSiteDelete_ValidateInput(t *testing.T) {
 		args              []string
 		flags             *common.CommandSiteDeleteFlags
 		cobraGenericFlags map[string]string
-		k8sObjects        []runtime.Object
-		skupperObjects    []runtime.Object
 		expectedError     string
 	}
 
@@ -126,22 +124,19 @@ func TestCmdSiteDelete_ValidateInput(t *testing.T) {
 
 func TestCmdSiteDelete_Run(t *testing.T) {
 	type test struct {
-		name                string
-		namespace           string
-		deleteName          string
-		k8sObjects          []runtime.Object
-		skupperObjects      []runtime.Object
-		skupperErrorMessage string
-		errorMessage        string
-		expectedNamespace   string
-		all                 bool
+		name              string
+		namespace         string
+		deleteName        string
+		errorMessage      string
+		expectedNamespace string
+		all               bool
 	}
 
 	testTable := []test{
 		{
 			name:              "run default",
 			deleteName:        "no-site",
-			errorMessage:      "error",
+			errorMessage:      "no such file or directory",
 			expectedNamespace: "default",
 			all:               false,
 		},
@@ -237,7 +232,7 @@ func TestCmdSiteDelete_Run(t *testing.T) {
 
 			err := command.Run()
 			if err != nil {
-				assert.Check(t, test.errorMessage == err.Error())
+				assert.Check(t, strings.HasSuffix(err.Error(), test.errorMessage))
 			} else {
 				assert.Check(t, err == nil)
 				assert.Equal(t, command.namespace, test.expectedNamespace)
diff --git a/internal/cmd/skupper/site/nonkube/site_generate_test.go b/internal/cmd/skupper/site/nonkube/site_generate_test.go
index 686932e5..7702961d 100644
--- a/internal/cmd/skupper/site/nonkube/site_generate_test.go
+++ b/internal/cmd/skupper/site/nonkube/site_generate_test.go
@@ -9,15 +9,12 @@ import (
 	"github.com/spf13/cobra"
 
 	"gotest.tools/v3/assert"
-	"k8s.io/apimachinery/pkg/runtime"
 )
 
 func TestNonKubeCmdSiteGenerate_ValidateInput(t *testing.T) {
 	type test struct {
 		name              string
 		args              []string
-		k8sObjects        []runtime.Object
-		skupperObjects    []runtime.Object
 		flags             *common.CommandSiteGenerateFlags
 		cobraGenericFlags map[string]string
 		expectedError     string
@@ -155,9 +152,6 @@ func TestNonKubeCmdSiteGenerate_InputToOptions(t *testing.T) {
 func TestNonKubeCmdSiteGenerate_Run(t *testing.T) {
 	type test struct {
 		name              string
-		k8sObjects        []runtime.Object
-		skupperObjects    []runtime.Object
-		skupperError      string
 		siteName          string
 		output            string
 		errorMessage      string
@@ -168,29 +162,21 @@ func TestNonKubeCmdSiteGenerate_Run(t *testing.T) {
 	testTable := []test{
 		{
 			name:              "runs ok",
-			k8sObjects:        nil,
-			skupperObjects:    nil,
 			siteName:          "my-site",
 			routerAccessName:  "ra-test",
 			linkAccessEnabled: true,
 			output:            "json",
 		},
 		{
-			name:           "runs ok with yaml output",
-			k8sObjects:     nil,
-			skupperObjects: nil,
-			siteName:       "test",
-			output:         "yaml",
-			skupperError:   "",
+			name:     "runs ok with yaml output",
+			siteName: "test",
+			output:   "yaml",
 		},
 		{
-			name:           "runs fails because the output format is not supported",
-			k8sObjects:     nil,
-			skupperObjects: nil,
-			siteName:       "test",
-			output:         "unsupported",
-			skupperError:   "",
-			errorMessage:   "format unsupported not supported",
+			name:         "runs fails because the output format is not supported",
+			siteName:     "test",
+			output:       "unsupported",
+			errorMessage: "format unsupported not supported",
 		},
 	}
 
diff --git a/internal/cmd/skupper/site/nonkube/site_status_test.go b/internal/cmd/skupper/site/nonkube/site_status_test.go
index de8ec664..b80bf0b6 100644
--- a/internal/cmd/skupper/site/nonkube/site_status_test.go
+++ b/internal/cmd/skupper/site/nonkube/site_status_test.go
@@ -21,8 +21,6 @@ func TestCmdSiteStatus_ValidateInput(t *testing.T) {
 		args              []string
 		flags             *common.CommandSiteStatusFlags
 		cobraGenericFlags map[string]string
-		k8sObjects        []runtime.Object
-		skupperObjects    []runtime.Object
 		expectedError     string
 	}
 
@@ -115,13 +113,10 @@ func TestCmdSiteStatus_ValidateInput(t *testing.T) {
 
 func TestCmdSiteStatus_Run(t *testing.T) {
 	type test struct {
-		name                string
-		siteName            string
-		k8sObjects          []runtime.Object
-		skupperObjects      []runtime.Object
-		skupperErrorMessage string
-		errorMessage        string
-		output              string
+		name         string
+		siteName     string
+		errorMessage string
+		output       string
 	}
 
 	if os.Getuid() == 0 {
diff --git a/internal/cmd/skupper/site/nonkube/site_update_test.go b/internal/cmd/skupper/site/nonkube/site_update_test.go
index 7bfdedf1..331dc83d 100644
--- a/internal/cmd/skupper/site/nonkube/site_update_test.go
+++ b/internal/cmd/skupper/site/nonkube/site_update_test.go
@@ -13,7 +13,6 @@ import (
 	"github.com/spf13/cobra"
 	"gotest.tools/v3/assert"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime"
 )
 
 func TestCmdSiteUpdate_ValidateInput(t *testing.T) {
@@ -205,15 +204,12 @@ func TestNonKubeCmdSiteUpdate_InputToOptions(t *testing.T) {
 
 func TestCmdSiteUpdate_Run(t *testing.T) {
 	type test struct {
-		name                string
-		namespace           string
-		k8sObjects          []runtime.Object
-		skupperObjects      []runtime.Object
-		skupperErrorMessage string
-		errorMessage        string
-		siteName            string
-		flags               common.CommandSiteUpdateFlags
-		linkAccessEnabled   bool
+		name              string
+		namespace         string
+		errorMessage      string
+		siteName          string
+		flags             common.CommandSiteUpdateFlags
+		linkAccessEnabled bool
 	}
 
 	testTable := []test{
diff --git a/internal/cmd/skupper/system/nonkube/system_apply_test.go b/internal/cmd/skupper/system/nonkube/system_apply_test.go
new file mode 100644
index 00000000..2ef127d0
--- /dev/null
+++ b/internal/cmd/skupper/system/nonkube/system_apply_test.go
@@ -0,0 +1,192 @@
+package nonkube
+
+import (
+	"bufio"
+	"fmt"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/testutils"
+	"github.com/skupperproject/skupper/internal/nonkube/client/fs"
+	"github.com/spf13/cobra"
+	"gotest.tools/v3/assert"
+	"log"
+	"os"
+	"testing"
+)
+
+func TestCmdSystemApply_ValidateInput(t *testing.T) {
+	type test struct {
+		name          string
+		args          []string
+		flags         *common.CommandSystemApplyFlags
+		expectedError string
+	}
+
+	testTable := []test{
+		{
+			name:          "arguments are not accepted",
+			args:          []string{"something"},
+			flags:         &common.CommandSystemApplyFlags{Filename: "-"},
+			expectedError: "This command does not accept arguments",
+		},
+		{
+			name:          "flag file is not provided",
+			args:          []string{},
+			expectedError: "You need to provide a file to apply or use standard input.\n Example: cat site.yaml | skupper system apply -f -",
+		},
+		{
+			name:          "file does not exist",
+			flags:         &common.CommandSystemApplyFlags{Filename: "file-does-not-exist.json"},
+			expectedError: "The file \"file-does-not-exist.json\" does not exist",
+		},
+		{
+			name:          "provided file is not a file but a directory",
+			flags:         &common.CommandSystemApplyFlags{Filename: "."},
+			expectedError: "The file has an unsupported extension, it should have one of the following: .yaml, .json\nThe file \".\" is a directory",
+		},
+		{
+			name:          "provided file has an unsupported extension",
+			flags:         &common.CommandSystemApplyFlags{Filename: "file.txt"},
+			expectedError: "The file has an unsupported extension, it should have one of the following: .yaml, .json\nThe file \"file.txt\" does not exist",
+		},
+	}
+
+	for _, test := range testTable {
+		t.Run(test.name, func(t *testing.T) {
+
+			command := &CmdSystemApply{Flags: test.flags}
+
+			testutils.CheckValidateInput(t, command, test.expectedError, test.args)
+		})
+	}
+}
+
+func TestCmdSystemApply_InputToOptions(t *testing.T) {
+
+	type test struct {
+		name              string
+		namespace         string
+		args              []string
+		flags             common.CommandSystemApplyFlags
+		expectedFilename  string
+		expectedNamespace string
+	}
+
+	testTable := []test{
+		{
+			name:              "filename is provided",
+			namespace:         "",
+			flags:             common.CommandSystemApplyFlags{Filename: "file.yaml"},
+			expectedFilename:  "file.yaml",
+			expectedNamespace: "default",
+		},
+		{
+			name:              "filename and namespace are provided",
+			namespace:         "east",
+			flags:             common.CommandSystemApplyFlags{Filename: "file.yaml"},
+			expectedFilename:  "file.yaml",
+			expectedNamespace: "east",
+		},
+		{
+			name:              "standard input is provided instead of a file",
+			namespace:         "",
+			flags:             common.CommandSystemApplyFlags{Filename: "-"},
+			expectedFilename:  "",
+			expectedNamespace: "default",
+		},
+	}
+
+	for _, test := range testTable {
+		t.Run(test.name, func(t *testing.T) {
+
+			cmd := newCmdSystemApplyWithMocks(false)
+			cmd.Namespace = test.namespace
+			cmd.Flags = &test.flags
+
+			cmd.InputToOptions()
+
+			assert.Check(t, cmd.file == test.expectedFilename)
+			assert.Check(t, cmd.Namespace == test.expectedNamespace)
+		})
+	}
+}
+
+func TestCmdSystemApply_Run(t *testing.T) {
+	type test struct {
+		name            string
+		inputParseFails bool
+		errorMessage    string
+	}
+
+	testTable := []test{
+		{
+			name:            "runs without errors",
+			inputParseFails: false,
+			errorMessage:    "",
+		},
+		{
+			name:            "input parsing fails",
+			inputParseFails: true,
+			errorMessage:    "Failed parsing the custom resources: fail",
+		},
+	}
+
+	for _, test := range testTable {
+		command := newCmdSystemApplyWithMocks(test.inputParseFails)
+
+		t.Run(test.name, func(t *testing.T) {
+
+			err := command.Run()
+			if err != nil {
+				assert.Check(t, test.errorMessage == err.Error())
+			} else {
+				assert.Check(t, err == nil)
+			}
+		})
+	}
+}
+
+// --- helper methods
+
+func newCmdSystemApplyWithMocks(inputParserFails bool) *CmdSystemApply {
+
+	cmd := &CmdSystemApply{}
+	cmd.CobraCmd = mockCmdSystemApplyFactory(common.PlatformLinux)
+
+	if inputParserFails {
+		cmd.ParseInput = mockInputParserFails
+	} else {
+		cmd.ParseInput = mockInputParserOK
+	}
+
+	return cmd
+}
+
+func mockInputParserOK(namespace string, reader *bufio.Reader, result *fs.InputFileResource) error {
+	return nil
+}
+
+func mockInputParserFails(namespace string, reader *bufio.Reader, result *fs.InputFileResource) error {
+	return fmt.Errorf("fail")
+}
+
+func mockCmdSystemApplyFactory(configuredPlatform common.Platform) *cobra.Command {
+
+	cmd := common.ConfigureCobraCommand(configuredPlatform, common.SkupperCmdDescription{}, nil, nil)
+
+	testInput := "test input"
+
+	r, w, err := os.Pipe()
+	if err != nil {
+		log.Fatalf("failed to create pipe: %v", err)
+	}
+
+	// Write to pipe in a goroutine to avoid blocking
+	go func() {
+		defer w.Close()
+		fmt.Fprint(w, testInput)
+	}()
+
+	cmd.SetIn(r)
+
+	return cmd
+}
diff --git a/internal/cmd/skupper/system/nonkube/system_delete_test.go b/internal/cmd/skupper/system/nonkube/system_delete_test.go
new file mode 100644
index 00000000..6208611e
--- /dev/null
+++ b/internal/cmd/skupper/system/nonkube/system_delete_test.go
@@ -0,0 +1,182 @@
+package nonkube
+
+import (
+	"fmt"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/testutils"
+	"github.com/spf13/cobra"
+	"gotest.tools/v3/assert"
+	"log"
+	"os"
+	"testing"
+)
+
+func TestCmdSystemDelete_ValidateInput(t *testing.T) {
+	type test struct {
+		name          string
+		args          []string
+		flags         *common.CommandSystemDeleteFlags
+		expectedError string
+	}
+
+	testTable := []test{
+		{
+			name:          "arguments are not accepted",
+			args:          []string{"something"},
+			flags:         &common.CommandSystemDeleteFlags{Filename: "-"},
+			expectedError: "This command does not accept arguments",
+		},
+		{
+			name:          "flag file is not provided",
+			args:          []string{},
+			expectedError: "You need to provide a file to delete custom resources or use standard input.\n Example: cat site.yaml | skupper system delete -f -",
+		},
+		{
+			name:          "file does not exist",
+			flags:         &common.CommandSystemDeleteFlags{Filename: "file-does-not-exist.json"},
+			expectedError: "The file \"file-does-not-exist.json\" does not exist",
+		},
+		{
+			name:          "provided file is not a file but a directory",
+			flags:         &common.CommandSystemDeleteFlags{Filename: "."},
+			expectedError: "The file has an unsupported extension, it should have one of the following: .yaml, .json\nThe file \".\" is a directory",
+		},
+		{
+			name:          "provided file has an unsupported extension",
+			flags:         &common.CommandSystemDeleteFlags{Filename: "file.txt"},
+			expectedError: "The file has an unsupported extension, it should have one of the following: .yaml, .json\nThe file \"file.txt\" does not exist",
+		},
+	}
+
+	for _, test := range testTable {
+		t.Run(test.name, func(t *testing.T) {
+
+			command := &CmdSystemDelete{Flags: test.flags}
+
+			testutils.CheckValidateInput(t, command, test.expectedError, test.args)
+		})
+	}
+}
+
+func TestCmdSystemDelete_InputToOptions(t *testing.T) {
+
+	type test struct {
+		name              string
+		namespace         string
+		args              []string
+		flags             common.CommandSystemDeleteFlags
+		expectedFilename  string
+		expectedNamespace string
+	}
+
+	testTable := []test{
+		{
+			name:              "filename is provided",
+			namespace:         "",
+			flags:             common.CommandSystemDeleteFlags{Filename: "file.yaml"},
+			expectedFilename:  "file.yaml",
+			expectedNamespace: "default",
+		},
+		{
+			name:              "filename and namespace are provided",
+			namespace:         "east",
+			flags:             common.CommandSystemDeleteFlags{Filename: "file.yaml"},
+			expectedFilename:  "file.yaml",
+			expectedNamespace: "east",
+		},
+		{
+			name:              "standard input is provided instead of a file",
+			namespace:         "",
+			flags:             common.CommandSystemDeleteFlags{Filename: "-"},
+			expectedFilename:  "",
+			expectedNamespace: "default",
+		},
+	}
+
+	for _, test := range testTable {
+		t.Run(test.name, func(t *testing.T) {
+
+			cmd := newCmdSystemDeleteWithMocks(false)
+			cmd.Namespace = test.namespace
+			cmd.Flags = &test.flags
+
+			cmd.InputToOptions()
+
+			assert.Check(t, cmd.file == test.expectedFilename)
+			assert.Check(t, cmd.Namespace == test.expectedNamespace)
+		})
+	}
+}
+
+func TestCmdSystemDelete_Run(t *testing.T) {
+	type test struct {
+		name            string
+		inputParseFails bool
+		errorMessage    string
+	}
+
+	testTable := []test{
+		{
+			name:            "runs without errors",
+			inputParseFails: false,
+			errorMessage:    "",
+		},
+		{
+			name:            "input parsing fails",
+			inputParseFails: true,
+			errorMessage:    "Failed parsing the custom resources: fail",
+		},
+	}
+
+	for _, test := range testTable {
+		command := newCmdSystemDeleteWithMocks(test.inputParseFails)
+
+		t.Run(test.name, func(t *testing.T) {
+
+			err := command.Run()
+			if err != nil {
+				assert.Check(t, test.errorMessage == err.Error())
+			} else {
+				assert.Check(t, err == nil)
+			}
+		})
+	}
+}
+
+// --- helper methods
+
+func newCmdSystemDeleteWithMocks(inputParserFails bool) *CmdSystemDelete {
+
+	cmd := &CmdSystemDelete{}
+	cmd.CobraCmd = mockCmdSystemDeleteFactory(common.PlatformLinux)
+
+	if inputParserFails {
+		cmd.ParseInput = mockInputParserFails
+	} else {
+		cmd.ParseInput = mockInputParserOK
+	}
+
+	return cmd
+}
+
+func mockCmdSystemDeleteFactory(configuredPlatform common.Platform) *cobra.Command {
+
+	cmd := common.ConfigureCobraCommand(configuredPlatform, common.SkupperCmdDescription{}, nil, nil)
+
+	testInput := "test input"
+
+	r, w, err := os.Pipe()
+	if err != nil {
+		log.Fatalf("failed to create pipe: %v", err)
+	}
+
+	// Write to pipe in a goroutine to avoid blocking
+	go func() {
+		defer w.Close()
+		fmt.Fprint(w, testInput)
+	}()
+
+	cmd.SetIn(r)
+
+	return cmd
+}
diff --git a/internal/cmd/skupper/system/system_test.go b/internal/cmd/skupper/system/system_test.go
index c2d7d6b5..f5df9ecb 100644
--- a/internal/cmd/skupper/system/system_test.go
+++ b/internal/cmd/skupper/system/system_test.go
@@ -53,6 +53,20 @@ func TestCmdSystemFactory(t *testing.T) {
 			},
 			command: CmdSystemUnInstallFactory(common.PlatformKubernetes),
 		},
+		{
+			name: "CmdSystemApplyFactory",
+			expectedFlagsWithDefaultValue: map[string]interface{}{
+				common.FlagNameFileName: "",
+			},
+			command: CmdSystemApplyFactory(common.PlatformKubernetes),
+		},
+		{
+			name: "CmdSystemDeleteFactory",
+			expectedFlagsWithDefaultValue: map[string]interface{}{
+				common.FlagNameFileName: "",
+			},
+			command: CmdSystemDeleteFactory(common.PlatformKubernetes),
+		},
 	}
 
 	for _, test := range testTable {
diff --git a/internal/cmd/skupper/token/nonkube/token_redeem_test.go b/internal/cmd/skupper/token/nonkube/token_redeem_test.go
index e5077281..d134881e 100644
--- a/internal/cmd/skupper/token/nonkube/token_redeem_test.go
+++ b/internal/cmd/skupper/token/nonkube/token_redeem_test.go
@@ -6,6 +6,7 @@ import (
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common/testutils"
 	"github.com/skupperproject/skupper/internal/nonkube/client/fs"
 	"github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
+	"github.com/skupperproject/skupper/pkg/nonkube/api"
 	"gotest.tools/v3/assert"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime/serializer/json"
@@ -30,9 +31,11 @@ func TestCmdTokenRedeem_ValidateInput(t *testing.T) {
 
 	defer os.Remove("/tmp/token-redeem.yaml")
 
-	tmpDir := filepath.Join(t.TempDir(), "/skupper")
-	err = os.Setenv("SKUPPER_OUTPUT_PATH", tmpDir)
-	assert.Check(t, err == nil)
+	if os.Getuid() == 0 {
+		api.DefaultRootDataHome = t.TempDir()
+	} else {
+		t.Setenv("XDG_DATA_HOME", t.TempDir())
+	}
 
 	testTable := []test{
 		{
@@ -86,9 +89,11 @@ func TestCmdTokenRedeem_Run(t *testing.T) {
 		errorType    string
 	}
 
-	tmpDir := filepath.Join(t.TempDir(), "/skupper")
-	err := os.Setenv("SKUPPER_OUTPUT_PATH", tmpDir)
-	assert.Check(t, err == nil)
+	if os.Getuid() == 0 {
+		api.DefaultRootDataHome = t.TempDir()
+	} else {
+		t.Setenv("XDG_DATA_HOME", t.TempDir())
+	}
 
 	tmpDirToken := t.TempDir()
 	tokenFile := filepath.Join(tmpDirToken, "token.yaml")
@@ -118,6 +123,7 @@ func TestCmdTokenRedeem_Run(t *testing.T) {
 		command.secretHandler = fs.NewSecretHandler(command.Namespace)
 		command.fileName = tokenFile
 
+		var err error
 		switch test.errorType {
 		case "malformedFile":
 			err = newMalformedTokenFile(tokenFile)
diff --git a/internal/cmd/skupper/version/kube/version_test.go b/internal/cmd/skupper/version/kube/version_test.go
index b77ffa32..4303d5cf 100644
--- a/internal/cmd/skupper/version/kube/version_test.go
+++ b/internal/cmd/skupper/version/kube/version_test.go
@@ -72,7 +72,7 @@ func TestCmdVersion_InputToOptions(t *testing.T) {
 			expected: true,
 		},
 		{
-			name:     "good defaul",
+			name:     "good default",
 			expected: false,
 		},
 	}
diff --git a/internal/kube/grants/redeem_test.go b/internal/kube/grants/redeem_test.go
index b16db041..e0a9d88a 100644
--- a/internal/kube/grants/redeem_test.go
+++ b/internal/kube/grants/redeem_test.go
@@ -379,7 +379,7 @@ func Test_RedeemAccessToken(t *testing.T) {
 			scheme:         "https",
 			tokenName:      "my-token",
 			defaultIssuer:  "i-dont-exist",
-			expectedStatus: "Controller got failed response: 500 (Internal Server Error) Could not get issuer for requested certficate",
+			expectedStatus: "Controller got failed response: 500 (Internal Server Error) Could not get issuer for requested certificate",
 		},
 	}
 	for _, tt := range tests {
diff --git a/internal/kube/securedaccess/access_test.go b/internal/kube/securedaccess/access_test.go
index 5a097953..1d8a13ef 100644
--- a/internal/kube/securedaccess/access_test.go
+++ b/internal/kube/securedaccess/access_test.go
@@ -2386,7 +2386,7 @@ func TestSecuredAccessManagerEnsure(t *testing.T) {
 				} else if err != nil {
 					t.Error(err)
 				} else {
-					// retrieve SecuredAccess instance from API and verify it natches expectations
+					// retrieve SecuredAccess instance from API and verify it matches expectations
 					actual, err := m.clients.GetSkupperClient().SkupperV2alpha1().SecuredAccesses(args.namespace).Get(context.Background(), args.name, metav1.GetOptions{})
 					if err != nil {
 						t.Error(err)
diff --git a/internal/kube/securedaccess/watchers_test.go b/internal/kube/securedaccess/watchers_test.go
index 66dc01b0..338a8aa9 100644
--- a/internal/kube/securedaccess/watchers_test.go
+++ b/internal/kube/securedaccess/watchers_test.go
@@ -178,7 +178,7 @@ func TestSecuredAccessRecovery(t *testing.T) {
 			k8sObjects: []runtime.Object{
 				service("mysvc", "test", selector(), "", servicePorts()),
 				route("mysvc-a", "test", "hoo", "foo", ""),
-				route("mysvc-b", "test", "haa", "bar", ""),
+				route("mysvc-b", "test", "hah", "bar", ""),
 			},
 			skupperObjects: []runtime.Object{
 				securedAccess("mysvc", "test", selector(), securedAccessPorts()),
diff --git a/internal/kube/site/site_test.go b/internal/kube/site/site_test.go
index b099aa83..03627ceb 100644
--- a/internal/kube/site/site_test.go
+++ b/internal/kube/site/site_test.go
@@ -1005,7 +1005,7 @@ func Test_CheckSecuredAccess(t *testing.T) {
 				if s.linkAccess["skupper-router"].Spec.AccessType == "loadbalancer" {
 					t.Errorf("Site.CheckSecuredAccess() linkAccess not updated")
 				}
-				// TBD adds to code coverge but doesn't return error if failed
+				// TBD adds to code coverage but doesn't return error if failed
 				if err = s.checkSecuredAccess(); err != nil {
 					t.Errorf("Site.CheckSecuredAccess() linkAccess not updated")
 				}
diff --git a/pkg/vanflow/eventsource/client_test.go b/pkg/vanflow/eventsource/client_test.go
index d6e2a8c9..703a0036 100644
--- a/pkg/vanflow/eventsource/client_test.go
+++ b/pkg/vanflow/eventsource/client_test.go
@@ -89,7 +89,7 @@ func TestClient(t *testing.T) {
 	func() {
 		defer func() {
 			if r := recover(); r != nil {
-				t.Errorf("client.Close() should be safe to call multiple times but paniced: %v", r)
+				t.Errorf("client.Close() should be safe to call multiple times but panicked: %v", r)
 			}
 		}()
 		client.Close()
diff --git a/pkg/vanflow/eventsource/discovery_test.go b/pkg/vanflow/eventsource/discovery_test.go
index fc12042d..1d4fcc65 100644
--- a/pkg/vanflow/eventsource/discovery_test.go
+++ b/pkg/vanflow/eventsource/discovery_test.go
@@ -51,7 +51,7 @@ func TestDiscoveryBasic(t *testing.T) {
 
 	// a "real" skupper router can drop beacon messages when there is not yet a
 	// consumer ready. retry sending initial beacons once when the
-	// implemenation is not a mock
+	// implementation is not a mock
 	retryOnceAfter := time.Now().Add(time.Hour * 48)
 	if _, isMock := tstSender.(interface{ IsMock() }); !isMock {
 		retryOnceAfter = time.Now().Add(500 * rtt)
diff --git a/pkg/vanflow/eventsource/manager_test.go b/pkg/vanflow/eventsource/manager_test.go
index 805e1964..a5841928 100644
--- a/pkg/vanflow/eventsource/manager_test.go
+++ b/pkg/vanflow/eventsource/manager_test.go
@@ -64,7 +64,7 @@ func TestManagerClient(t *testing.T) {
 		}
 	})
 
-	t.Run("will syncronize records", func(t *testing.T) {
+	t.Run("will synchronize records", func(t *testing.T) {
 		poll.WaitOn(t, func(t poll.LogT) poll.Result {
 			actual := clientStor.List()
 			expected := logStor.List()
diff --git a/pkg/vanflow/messages_test.go b/pkg/vanflow/messages_test.go
index d9f9b8da..695fcc30 100644
--- a/pkg/vanflow/messages_test.go
+++ b/pkg/vanflow/messages_test.go
@@ -50,7 +50,7 @@ func TestDecode(t *testing.T) {
 			In:    &amqp.Message{Properties: &amqp.MessageProperties{Subject: ptrTo("A")}},
 			Error: "cannot decode message",
 		}, {
-			Name: "emtpy record",
+			Name: "empty record",
 			In:   func() *amqp.Message { m, _ := RecordMessage{}.Encode(); return m }(),
 			Out:  RecordMessage{MessageProps: MessageProps{Subject: "RECORD"}},
 		}, {
diff --git a/tests/README.md b/tests/README.md
index 2baac92b..96a97107 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -138,7 +138,7 @@ make test TEST="hello-world"
 
 - This will run the `hello-world` test located in the `e2e/hello-world` directory, activating the virtual environment and running the test playbook.
 
-### Aditional Configuration
+### Additional Configuration
 
 1. **vars.yml file**: Create a `vars.yml` file in the repository root to set extra variables for the tests.
 
diff --git a/tests/e2e/scenarios/ha/test.yml b/tests/e2e/scenarios/ha/test.yml
index 0a033ba9..a08206f1 100644
--- a/tests/e2e/scenarios/ha/test.yml
+++ b/tests/e2e/scenarios/ha/test.yml
@@ -155,7 +155,7 @@
           when:
             - "'west' in inventory_hostname"
 
-        - name: "[ HATest - RunTest ] Failling the test if num_requests = 0 or num_failures > 5% "
+        - name: "[ HATest - RunTest ] Failing the test if num_requests = 0 or num_failures > 5% "
           fail:
             msg: "The test has FAILED - num_requests: {{ num_requests }}, num_failures: {{ num_failures }}"
           when:
diff --git a/tests/e2e/scenarios/redis/inventory/host_vars/east.yml b/tests/e2e/scenarios/redis/inventory/host_vars/east.yml
index 48b2cff7..50b3c5f4 100644
--- a/tests/e2e/scenarios/redis/inventory/host_vars/east.yml
+++ b/tests/e2e/scenarios/redis/inventory/host_vars/east.yml
@@ -6,7 +6,7 @@ kubeconfig: "{{ kubeconfig_2 }}"
 # Namespace configuration
 namespace_name: redis-east
 
-# Wast CRs
+# East CRs
 site: "{{ playbook_dir }}/resources/east/site-east.yml"
 connector: "{{ playbook_dir }}/resources/east/connector-east.yml"
 listener: "{{ playbook_dir }}/resources/east/listener-east.yml"