Repository: skupperproject/skupper (go)

Issue:
Unsupported `linkAccess` Type Causes Site to Remain in Pending State
**Describe the bug**
When setting an unsupported value (`rocket`) for the `linkAccess` property in a Skupper `Site` resource, the site remains in a `Pending` state. The controller logs this issue as an `INFO` message instead of an `ERROR`, making it difficult to identify the root cause. The lack of validation or clear feedback for invalid `linkAccess` values contributes to user confusion.

**How To Reproduce**
Steps to reproduce the behavior:
1. Install Skupper using the following configuration:
   - Repository: `https://github.com/skupperproject/skupper.git`
   - Branch: `v2`
   - Release name: `skupper-setup`
2. Deploy a `Site` resource with the following spec:
   ```yaml
   apiVersion: skupper.io/v2alpha1
   kind: Site
   metadata:
     name: hello-world-west
     namespace: zago-hello-world-west
   spec:
     linkAccess: rocket
   ```
3. Check the site status:
   ```bash
   kubectl get site -n zago-hello-world-west
   ```
   Output:
   ```
   NAME               STATUS    SITES IN NETWORK   MESSAGE
   hello-world-west   Pending   1                  Pending
   ```
4. Review the controller logs:
   ```
   time=2024-11-22T17:33:10.777Z level=INFO msg="Unsupported access type \"rocket\" in SecuredAccess zago-hello-world-west/skupper-router-1"
   ```

**Expected behavior**
- The controller should log an `ERROR` message instead of `INFO` for unsupported `linkAccess` values.
- The user should receive clear feedback in the logs indicating that the value is invalid.
- The `linkAccess` field should be validated against the documented valid values (`none`, `default`, `route`, `loadbalancer`), and the site should fail to apply the configuration if the value is invalid.

**Environment details**
- Skupper CLI: v2
- Skupper Operator (if applicable): v2
- Platform: Kubernetes (local cluster)

**Additional context**
The valid values for `linkAccess`, as per the documentation, are:
- `none`: No linking to this site is permitted.
- `default`: Use the default link access for the current platform.
  - On OpenShift, the default is `route`.
  - On Kubernetes, the default is `loadbalancer`.
- `route`: Use an OpenShift route (OpenShift only).
- `loadbalancer`: Use a Kubernetes load balancer (Kubernetes only).

Improving logging and validation for `linkAccess` will help users quickly identify and resolve configuration issues.


Pull request:
Improved handling of invalid linkAccess values
Fixes #1797 

Patch:
diff --git a/.gitignore b/.gitignore
index d2fedbd3..88d33a1c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,7 +5,7 @@
 *.so
 *.dylib
 /skupper
-/config-sync
+/kube-adaptor
 /manifest
 /generate-doc
 /bootstrap
diff --git a/Dockerfile.config-sync b/Dockerfile.kube-adaptor
similarity index 82%
rename from Dockerfile.config-sync
rename to Dockerfile.kube-adaptor
index f678aa6e..9b5a2602 100644
--- a/Dockerfile.config-sync
+++ b/Dockerfile.kube-adaptor
@@ -10,7 +10,7 @@ RUN go mod download
 
 COPY . .
 ENV CGO_ENABLED=0
-RUN make GOARCH=$TARGETARCH build-config-sync
+RUN make GOARCH=$TARGETARCH build-kube-adaptor
 
 FROM --platform=$TARGETPLATFORM registry.access.redhat.com/ubi9-minimal
 LABEL \
@@ -24,5 +24,5 @@ RUN useradd --uid 10000 runner
 USER 10000
 
 WORKDIR /app
-COPY --from=builder /go/src/app/config-sync .
-CMD ["/app/config-sync"]
+COPY --from=builder /go/src/app/kube-adaptor .
+CMD ["/app/kube-adaptor"]
diff --git a/Makefile b/Makefile
index 10b8e89b..181d68d8 100644
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@ GOARCH ?= amd64
 REGISTRY := quay.io/skupper
 IMAGE_TAG := v2-latest
 PLATFORMS ?= linux/amd64,linux/arm64
-CONTAINERFILES := Dockerfile.cli Dockerfile.config-sync Dockerfile.controller Dockerfile.network-observer
+CONTAINERFILES := Dockerfile.cli Dockerfile.kube-adaptor Dockerfile.controller Dockerfile.network-observer
 SHARED_IMAGE_LABELS = \
     --label "org.opencontainers.image.created=$(shell TZ=GMT date --iso-8601=seconds)" \
 	--label "org.opencontainers.image.url=https://skupper.io/" \
@@ -25,7 +25,7 @@ DOCKER := docker
 SKOPEO := skopeo
 PODMAN := podman
 
-all: build-cli build-config-sync build-controller build-network-observer update-helm-crd
+all: build-cli build-kube-adaptor build-controller build-network-observer update-helm-crd
 
 build-cli:
 	GOOS=${GOOS} GOARCH=${GOARCH} go build -ldflags="${LDFLAGS}"  -o skupper ./cmd/skupper
@@ -33,8 +33,8 @@ build-cli:
 build-controller:
 	GOOS=${GOOS} GOARCH=${GOARCH} go build -ldflags="${LDFLAGS}"  -o controller ./cmd/controller
 
-build-config-sync:
-	GOOS=${GOOS} GOARCH=${GOARCH} go build -ldflags="${LDFLAGS}"  -o config-sync ./cmd/config-sync
+build-kube-adaptor:
+	GOOS=${GOOS} GOARCH=${GOARCH} go build -ldflags="${LDFLAGS}"  -o kube-adaptor ./cmd/kube-adaptor
 
 build-network-observer:
 	GOOS=${GOOS} GOARCH=${GOARCH} go build -ldflags="${LDFLAGS}"  -o network-observer ./cmd/network-observer
@@ -110,7 +110,7 @@ cover:
 		-coverprofile cover.out \
 		./...
 
-generate-manifest: build-cmd
+generate-manifest: build-cli
 	./skupper version -o json > manifest.json
 
 generate-doc: build-doc-generator
@@ -120,6 +120,6 @@ update-helm-crd:
 	./scripts/update-helm-crds.sh
 
 clean:
-	rm -rf skupper controller config-sync \
+	rm -rf skupper controller kube-adaptor \
 		network-observer generate-doc \
 		cover.out oci-archives
diff --git a/cmd/controller/deploy_cluster_scope.yaml b/cmd/controller/deploy_cluster_scope.yaml
index 6394509a..aef4ab13 100644
--- a/cmd/controller/deploy_cluster_scope.yaml
+++ b/cmd/controller/deploy_cluster_scope.yaml
@@ -218,8 +218,8 @@ spec:
         command: ["/app/controller"]
         args: ["-enable-grants", "-grant-server-autoconfigure"]
         env:
-        - name: SKUPPER_CONFIG_SYNC_IMAGE
-          value: quay.io/skupper/config-sync:v2-latest
+        - name: SKUPPER_ADAPTOR_IMAGE
+          value: quay.io/skupper/kube-adaptor:v2-latest
         securityContext:
           capabilities:
             drop:
diff --git a/cmd/controller/deploy_namespace_scope.yaml b/cmd/controller/deploy_namespace_scope.yaml
index 83c9c1dc..f8d238cc 100644
--- a/cmd/controller/deploy_namespace_scope.yaml
+++ b/cmd/controller/deploy_namespace_scope.yaml
@@ -206,8 +206,8 @@ spec:
         command: ["/app/controller"]
         args: ["-enable-grants", "-grant-server-autoconfigure"]
         env:
-        - name: SKUPPER_CONFIG_SYNC_IMAGE
-          value: quay.io/skupper/config-sync:v2-latest
+        - name: SKUPPER_ADAPTOR_IMAGE
+          value: quay.io/skupper/kube-adaptor:v2-latest
         - name: WATCH_NAMESPACE
           valueFrom:
              fieldRef:
diff --git a/cmd/config-sync/main.go b/cmd/kube-adaptor/main.go
similarity index 91%
rename from cmd/config-sync/main.go
rename to cmd/kube-adaptor/main.go
index 3510a763..e51d602d 100644
--- a/cmd/config-sync/main.go
+++ b/cmd/kube-adaptor/main.go
@@ -61,7 +61,7 @@ func main() {
 	}
 
 	// Startup message
-	log.Printf("CONFIG_SYNC: Version: %s", version.Version)
+	log.Printf("Version: %s", version.Version)
 
 	// set up signals so we handle the first shutdown signal gracefully
 	stopCh := SetupSignalHandler()
@@ -78,13 +78,13 @@ func main() {
 		os.Exit(0)
 	}
 
-	log.Println("CONFIG_SYNC: Waiting for Skupper router to be ready")
+	log.Println("Waiting for Skupper router to be ready")
 	_, err = kube.WaitForPodsSelectorStatus(cli.GetNamespace(), cli.Kube, "skupper.io/component=router", corev1.PodRunning, time.Second*180, time.Second*5)
 	if err != nil {
 		log.Fatal("Error waiting for router pods to be ready ", err.Error())
 	}
 
-	log.Println("CONFIG_SYNC: Starting collector...")
+	log.Println("Starting collector...")
 	go adaptor.StartCollector(cli)
 
 	//start health check
@@ -95,10 +95,10 @@ func main() {
 	go http.ListenAndServe(":9191", nil)
 
 	configSync := adaptor.NewConfigSync(cli, cli.GetNamespace(), configDir, configMapName)
-	log.Println("CONFIG_SYNC: Starting controller loop...")
+	log.Println("Starting controller loop...")
 	configSync.Start(stopCh)
 
 	<-stopCh
-	log.Println("CONFIG_SYNC: Shutting down...")
+	log.Println("Shutting down...")
 	configSync.Stop()
 }
diff --git a/deployments/helm/templates/deploy_controller.yaml b/deployments/helm/templates/deploy_controller.yaml
index dba3b09b..707768e3 100644
--- a/deployments/helm/templates/deploy_controller.yaml
+++ b/deployments/helm/templates/deploy_controller.yaml
@@ -238,8 +238,8 @@ spec:
           command: ["/app/controller"]
           args: ["-enable-grants", "-grant-server-autoconfigure"]
           env:
-            - name: SKUPPER_CONFIG_SYNC_IMAGE
-              value: {{ .Values.images.configSync}}
+            - name: SKUPPER_KUBE_ADAPTOR_IMAGE
+              value: {{ .Values.images.adaptor}}
             {{ if eq .Values.scope "namespace"}}
             - name: WATCH_NAMESPACE
               valueFrom:
diff --git a/deployments/helm/values.yaml b/deployments/helm/values.yaml
index 8f0aa983..50f0871b 100644
--- a/deployments/helm/values.yaml
+++ b/deployments/helm/values.yaml
@@ -1,6 +1,6 @@
 images:
   controller: "quay.io/skupper/controller:v2-latest"
-  configSync: "quay.io/skupper/config-sync:v2-latest"
+  adaptor: "quay.io/skupper/kube-adaptor:v2-latest"
 
 # available options: cluster, namespace
 scope: cluster
\ No newline at end of file
diff --git a/internal/cmd/skupper/token/kube/token_issue.go b/internal/cmd/skupper/token/kube/token_issue.go
index 1400a0aa..d6a0873e 100644
--- a/internal/cmd/skupper/token/kube/token_issue.go
+++ b/internal/cmd/skupper/token/kube/token_issue.go
@@ -46,6 +46,7 @@ func (cmd *CmdTokenIssue) ValidateInput(args []string) []error {
 	resourceStringValidator := validator.NewResourceStringValidator()
 	tokenStringValidator := validator.NewFilePathStringValidator()
 	timeoutValidator := validator.NewTimeoutInSecondsValidator()
+	expirationValidator := validator.NewExpirationInSecondsValidator()
 
 	// Validate token file name
 	if len(args) < 1 {
@@ -95,13 +96,12 @@ func (cmd *CmdTokenIssue) ValidateInput(args []string) []error {
 	}
 
 	// Validate flags
-	//TBD is there a limit to number of redemptions
 	if cmd.Flags != nil && cmd.Flags.RedemptionsAllowed < 1 {
 		validationErrors = append(validationErrors, fmt.Errorf("number of redemptions is not valid"))
 	}
 
 	if cmd.Flags != nil && cmd.Flags.ExpirationWindow.String() != "" {
-		ok, err := timeoutValidator.Evaluate(cmd.Flags.ExpirationWindow)
+		ok, err := expirationValidator.Evaluate(cmd.Flags.ExpirationWindow)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("expiration time is not valid: %s", err))
 		}
diff --git a/pkg/images/image_utils.go b/pkg/images/image_utils.go
index faffd3c2..7fbcc147 100644
--- a/pkg/images/image_utils.go
+++ b/pkg/images/image_utils.go
@@ -18,13 +18,13 @@ type SkupperImage struct {
 const (
 	RouterImageEnvKey             string = "SKUPPER_ROUTER_IMAGE"
 	ControllerImageEnvKey         string = "SKUPPER_CONTROLLER_IMAGE"
-	ConfigSyncImageEnvKey         string = "SKUPPER_CONFIG_SYNC_IMAGE"
+	AdaptorImageEnvKey            string = "SKUPPER_ADAPTOR_IMAGE"
 	NetworkObserverImageEnvKey    string = "SKUPPER_NETWORK_OBSERVER_IMAGE"
 	CliImageEnvKey                string = "SKUPPER_CLI_IMAGE"
 	PrometheusServerImageEnvKey   string = "PROMETHEUS_SERVER_IMAGE"
 	OauthProxyImageEnvKey         string = "OAUTH_PROXY_IMAGE"
 	RouterPullPolicyEnvKey        string = "SKUPPER_ROUTER_IMAGE_PULL_POLICY"
-	ConfigSyncPullPolicyEnvKey    string = "SKUPPER_CONFIG_SYNC_IMAGE_PULL_POLICY"
+	AdaptorPullPolicyEnvKey       string = "SKUPPER_ADAPTOR_IMAGE_PULL_POLICY"
 	OauthProxyPullPolicyEnvKey    string = "OAUTH_PROXY_IMAGE_PULL_POLICY"
 	SkupperImageRegistryEnvKey    string = "SKUPPER_IMAGE_REGISTRY"
 	PrometheusImageRegistryEnvKey string = "PROMETHEUS_IMAGE_REGISTRY"
@@ -104,25 +104,25 @@ func GetCliImageName() string {
 	}
 }
 
-func GetConfigSyncImageDetails() types.ImageDetails {
+func GetAdaptorImageDetails() types.ImageDetails {
 	return types.ImageDetails{
-		Name:       GetConfigSyncImageName(),
-		PullPolicy: GetConfigSyncImagePullPolicy(),
+		Name:       GetAdaptorImageName(),
+		PullPolicy: GetAdaptorImagePullPolicy(),
 	}
 }
 
-func GetConfigSyncImageName() string {
-	image := os.Getenv(ConfigSyncImageEnvKey)
+func GetAdaptorImageName() string {
+	image := os.Getenv(AdaptorImageEnvKey)
 	if image == "" {
 		imageRegistry := GetImageRegistry()
-		return strings.Join([]string{imageRegistry, ConfigSyncImageName}, "/")
+		return strings.Join([]string{imageRegistry, AdaptorImageName}, "/")
 	} else {
 		return image
 	}
 }
 
-func GetConfigSyncImagePullPolicy() string {
-	return getPullPolicy(ConfigSyncPullPolicyEnvKey)
+func GetAdaptorImagePullPolicy() string {
+	return getPullPolicy(AdaptorPullPolicyEnvKey)
 }
 
 func GetPrometheusServerImageName() string {
@@ -234,7 +234,7 @@ func GetImages(component string, enableSHA bool) []SkupperImage {
 	case "router":
 		// skupper router has two components
 		names[RouterImageEnvKey] = RouterImageName
-		names[ConfigSyncImageEnvKey] = ConfigSyncImageName
+		names[AdaptorImageEnvKey] = AdaptorImageName
 		registry = GetImageRegistry()
 	case "controller":
 		names[ControllerImageEnvKey] = ControllerImageName
diff --git a/pkg/images/images.go b/pkg/images/images.go
index 0e7c7560..4d0caf83 100644
--- a/pkg/images/images.go
+++ b/pkg/images/images.go
@@ -4,7 +4,7 @@ const (
 	DefaultImageRegistry     string = "quay.io/skupper"
 	RouterImageName          string = "skupper-router:main"
 	ControllerImageName      string = "controller:v2-latest"
-	ConfigSyncImageName      string = "config-sync:v2-latest"
+	AdaptorImageName         string = "kube-adaptor:v2-latest"
 	NetworkObserverImageName string = "network-observer:v2-latest"
 	CliImageName             string = "cli:v2-latest"
 
diff --git a/pkg/kube/securedaccess/access.go b/pkg/kube/securedaccess/access.go
index a490308d..723bb9d0 100644
--- a/pkg/kube/securedaccess/access.go
+++ b/pkg/kube/securedaccess/access.go
@@ -85,6 +85,11 @@ func NewSecuredAccessManager(clients internalclient.Clients, certMgr certificate
 	return mgr
 }
 
+func (m *SecuredAccessManager) IsValidAccessType(accessType string) bool {
+	_, ok := m.enabledAccessTypes[accessType]
+	return ok
+}
+
 func (m *SecuredAccessManager) Ensure(namespace string, name string, spec skupperv2alpha1.SecuredAccessSpec, annotations map[string]string, refs []metav1.OwnerReference) error {
 	key := fmt.Sprintf("%s/%s", namespace, name)
 	if current, ok := m.definitions[key]; ok {
diff --git a/pkg/kube/site/resources/apply.go b/pkg/kube/site/resources/apply.go
index 5abbdd13..cf5a69ae 100644
--- a/pkg/kube/site/resources/apply.go
+++ b/pkg/kube/site/resources/apply.go
@@ -49,14 +49,14 @@ func resourceTemplates(site *skupperv2alpha1.Site, group string) []resource.Temp
 }
 
 type CoreParams struct {
-	SiteId          string
-	SiteName        string
-	Group           string
-	Replicas        int
-	ServiceAccount  string
-	ConfigDigest    string
-	RouterImage     skuppertypes.ImageDetails
-	ConfigSyncImage skuppertypes.ImageDetails
+	SiteId         string
+	SiteName       string
+	Group          string
+	Replicas       int
+	ServiceAccount string
+	ConfigDigest   string
+	RouterImage    skuppertypes.ImageDetails
+	AdaptorImage   skuppertypes.ImageDetails
 }
 
 func configDigest(config *skupperv2alpha1.SiteSpec) string {
@@ -81,14 +81,14 @@ func configDigest(config *skupperv2alpha1.SiteSpec) string {
 
 func getCoreParams(site *skupperv2alpha1.Site, group string) CoreParams {
 	return CoreParams{
-		SiteId:          site.GetSiteId(),
-		SiteName:        site.Name,
-		Group:           group,
-		Replicas:        1,
-		ServiceAccount:  site.Spec.GetServiceAccount(),
-		ConfigDigest:    configDigest(&site.Spec),
-		RouterImage:     images.GetRouterImageDetails(),
-		ConfigSyncImage: images.GetConfigSyncImageDetails(),
+		SiteId:         site.GetSiteId(),
+		SiteName:       site.Name,
+		Group:          group,
+		Replicas:       1,
+		ServiceAccount: site.Spec.GetServiceAccount(),
+		ConfigDigest:   configDigest(&site.Spec),
+		RouterImage:    images.GetRouterImageDetails(),
+		AdaptorImage:   images.GetAdaptorImageDetails(),
 	}
 }
 
diff --git a/pkg/kube/site/resources/skupper-router-deployment.yaml b/pkg/kube/site/resources/skupper-router-deployment.yaml
index 81b93fdd..d43bb22c 100644
--- a/pkg/kube/site/resources/skupper-router-deployment.yaml
+++ b/pkg/kube/site/resources/skupper-router-deployment.yaml
@@ -77,8 +77,6 @@ spec:
           successThreshold: 1
           timeoutSeconds: 1
         volumeMounts:
-        - mountPath: /etc/skupper-router-certs/skupper-amqps/
-          name: skupper-local-server
         - mountPath: /etc/skupper-router-certs
           name: skupper-router-certs
       - env:
@@ -95,9 +93,9 @@ spec:
           value: {{ .Group }}
         - name: SKUPPER_ROUTER_DEPLOYMENT
           value: {{ .Group }}
-        image: {{ .ConfigSyncImage.Name }}
-        imagePullPolicy: {{ .ConfigSyncImage.PullPolicy }}
-        name: config-sync
+        image: {{ .AdaptorImage.Name }}
+        imagePullPolicy: {{ .AdaptorImage.PullPolicy }}
+        name: kube-adaptor
         readinessProbe:
           failureThreshold: 3
           httpGet:
@@ -115,18 +113,14 @@ spec:
       - env:
         - name: SKUPPER_ROUTER_CONFIG
           value: {{ .Group }}
-        image: {{ .ConfigSyncImage.Name }}
-        imagePullPolicy: {{ .ConfigSyncImage.PullPolicy }}
+        image: {{ .AdaptorImage.Name }}
+        imagePullPolicy: {{ .AdaptorImage.PullPolicy }}
         name: config-init
-        command: ["/app/config-sync", "-init"]
+        command: ["/app/kube-adaptor", "-init"]
         volumeMounts:
         - mountPath: /etc/skupper-router-certs
           name: skupper-router-certs
       serviceAccount: {{ .ServiceAccount }}
       volumes:
-      - name: skupper-local-server
-        secret:
-          defaultMode: 420
-          secretName: skupper-local-server
       - emptyDir: {}
         name: skupper-router-certs
diff --git a/pkg/kube/site/site.go b/pkg/kube/site/site.go
index 8b83a18b..6237422a 100644
--- a/pkg/kube/site/site.go
+++ b/pkg/kube/site/site.go
@@ -27,6 +27,7 @@ import (
 
 type SecuredAccessFactory interface {
 	Ensure(namespace string, name string, spec skupperv2alpha1.SecuredAccessSpec, annotations map[string]string, refs []metav1.OwnerReference) error
+	IsValidAccessType(accessType string) bool
 }
 
 type Site struct {
@@ -61,6 +62,13 @@ func NewSite(namespace string, controller *kube.Controller, certs certificates.C
 	}
 }
 
+func (s *Site) verifySiteSpec(site *skupperv2alpha1.Site) error {
+	if site.Spec.LinkAccess != "" && site.Spec.LinkAccess != "none" && site.Spec.LinkAccess != "default" && !s.access.IsValidAccessType(site.Spec.LinkAccess) {
+		return fmt.Errorf("Unsupported value for LinkAccess: %s", site.Spec.LinkAccess)
+	}
+	return nil
+}
+
 func (s *Site) StartRecovery(site *skupperv2alpha1.Site) error {
 	//TODO: check version and perform any necessary update tasks
 	return s.reconcile(site, true)
@@ -99,6 +107,9 @@ func (s *Site) reconcile(siteDef *skupperv2alpha1.Site, inRecovery bool) error {
 		slog.String("namespace", siteDef.Namespace),
 		slog.String("name", siteDef.Name),
 		slog.String("id", s.site.GetSiteId()))
+	if err := s.verifySiteSpec(siteDef); err != nil {
+		return err
+	}
 	// ensure necessary resources:
 	// 1. skupper-internal configmap
 	if !s.initialised {
@@ -851,6 +862,13 @@ func (s *Site) updateConfigured(err error) error {
 	}
 	if s.site.SetConfigured(err) {
 		changed = true
+		if err != nil {
+			s.logger.Error("Error configuring site",
+				slog.String("namespace", s.site.Namespace),
+				slog.String("name", s.site.Name),
+				slog.String("id", s.site.GetSiteId()),
+				slog.Any("error", err))
+		}
 	}
 	if changed {
 		return s.updateSiteStatus()
diff --git a/pkg/utils/configs/manifest.go b/pkg/utils/configs/manifest.go
index e39a1990..dd788991 100644
--- a/pkg/utils/configs/manifest.go
+++ b/pkg/utils/configs/manifest.go
@@ -99,9 +99,9 @@ func getEnvironmentVariableMap() *map[string]string {
 		envVariables[images.ControllerImageEnvKey] = controllerImage
 	}
 
-	configSyncImage := os.Getenv(images.ConfigSyncImageEnvKey)
-	if configSyncImage != "" {
-		envVariables[images.ConfigSyncImageEnvKey] = configSyncImage
+	adaptorImage := os.Getenv(images.AdaptorImageEnvKey)
+	if adaptorImage != "" {
+		envVariables[images.AdaptorImageEnvKey] = adaptorImage
 	}
 
 	networkObserverImage := os.Getenv(images.NetworkObserverImageEnvKey)
diff --git a/pkg/utils/validator/simple_validator.go b/pkg/utils/validator/simple_validator.go
index a52b6652..e1883220 100644
--- a/pkg/utils/validator/simple_validator.go
+++ b/pkg/utils/validator/simple_validator.go
@@ -148,6 +148,12 @@ func NewTimeoutInSecondsValidator() *DurationValidator {
 	}
 }
 
+func NewExpirationInSecondsValidator() *DurationValidator {
+	return &DurationValidator{
+		MinDuration: time.Minute * 1,
+	}
+}
+
 func (i DurationValidator) Evaluate(value time.Duration) (bool, error) {
 
 	if value < i.MinDuration {


Test patch:
diff --git a/internal/cmd/skupper/token/kube/token_issue_test.go b/internal/cmd/skupper/token/kube/token_issue_test.go
index 7e17ec69..f9a538b2 100644
--- a/internal/cmd/skupper/token/kube/token_issue_test.go
+++ b/internal/cmd/skupper/token/kube/token_issue_test.go
@@ -392,9 +392,9 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 			name: "expiration is not valid",
 			args: []string{"~/token.yaml"},
 			flags: common.CommandTokenIssueFlags{
-				ExpirationWindow:   0 * time.Minute,
+				ExpirationWindow:   10 * time.Second,
 				RedemptionsAllowed: 1,
-				Timeout:            60 * time.Second,
+				Timeout:            10 * time.Second,
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.SiteList{
@@ -426,7 +426,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			expectedErrors: []string{"expiration time is not valid: duration must not be less than 10s; got 0s"},
+			expectedErrors: []string{"expiration time is not valid: duration must not be less than 1m0s; got 10s"},
 		},
 		{
 			name: "timeout is not valid",
diff --git a/pkg/utils/configs/manifest_test.go b/pkg/utils/configs/manifest_test.go
index e619aaea..e11b38e8 100644
--- a/pkg/utils/configs/manifest_test.go
+++ b/pkg/utils/configs/manifest_test.go
@@ -25,7 +25,7 @@ func TestManifestManager(t *testing.T) {
 			title: "configured manifest has different images that the default manifest",
 			envVariablesWithValue: []string{
 				images.SkupperImageRegistryEnvKey,
-				images.ConfigSyncImageEnvKey,
+				images.AdaptorImageEnvKey,
 				images.RouterImageEnvKey,
 				images.ControllerImageEnvKey,
 			},
@@ -37,7 +37,7 @@ func TestManifestManager(t *testing.T) {
 							Version:   "main",
 							Images: []images.SkupperImage{
 								{
-									Name: "SKUPPER_CONFIG_SYNC_IMAGE_TESTING",
+									Name: "SKUPPER_ADAPTOR_IMAGE_TESTING",
 								},
 								{
 									Name: "SKUPPER_ROUTER_IMAGE_TESTING",
@@ -64,7 +64,7 @@ func TestManifestManager(t *testing.T) {
 							Version:   "",
 							Images: []images.SkupperImage{
 								{
-									Name: strings.Join([]string{images.DefaultImageRegistry, images.ConfigSyncImageName}, "/"),
+									Name: strings.Join([]string{images.DefaultImageRegistry, images.AdaptorImageName}, "/"),
 								},
 								{
 									Name: strings.Join([]string{images.DefaultImageRegistry, images.RouterImageName}, "/"),
@@ -75,7 +75,7 @@ func TestManifestManager(t *testing.T) {
 				},
 				Variables: &map[string]string{
 					images.SkupperImageRegistryEnvKey: "SKUPPER_IMAGE_REGISTRY_TESTING",
-					images.ConfigSyncImageEnvKey:      "SKUPPER_CONFIG_SYNC_IMAGE_TESTING",
+					images.AdaptorImageEnvKey:         "SKUPPER_ADAPTOR_IMAGE_TESTING",
 					images.RouterImageEnvKey:          "SKUPPER_ROUTER_IMAGE_TESTING",
 					images.ControllerImageEnvKey:      "SKUPPER_CONTROLLER_IMAGE_TESTING",
 				},
@@ -92,7 +92,7 @@ func TestManifestManager(t *testing.T) {
 							Version:   "main",
 							Images: []images.SkupperImage{
 								{
-									Name: strings.Join([]string{images.DefaultImageRegistry, images.ConfigSyncImageName}, "/"),
+									Name: strings.Join([]string{images.DefaultImageRegistry, images.AdaptorImageName}, "/"),
 								},
 								{
 									Name: strings.Join([]string{images.DefaultImageRegistry, images.RouterImageName}, "/"),
@@ -110,7 +110,7 @@ func TestManifestManager(t *testing.T) {
 							Version:   "main",
 							Images: []images.SkupperImage{
 								{
-									Name: strings.Join([]string{images.DefaultImageRegistry, images.ConfigSyncImageName}, "/"),
+									Name: strings.Join([]string{images.DefaultImageRegistry, images.AdaptorImageName}, "/"),
 								},
 								{
 									Name: strings.Join([]string{images.DefaultImageRegistry, images.RouterImageName}, "/"),