Repository: skupperproject/skupper
Pull request #1831 (2024-12-11 17:48:52)

Export helm templates as an artifact for releases
**Changes:**
- Moved the helm chart to the directory `charts`, to follow current conventions and to allow the inclusion of more charts.
- Added AppVersion: 2.0.0 (that differs from helm chart version, 1.0.0).
- Exported the two variants of the template scope: namespace, cluster.
- Added additional step in the circle CI config file to include the files as part of the release artifact.
- Deleted deployment yamls to avoid duplication. 

nluaces: @grs this is not done yet, but what do you think, in the case of the cluster scope, about not creating the skupper namespace, and deploy the controller in the namespace that the user has configured?
grs: > @grs this is not done yet, but what do you think, in the case of the cluster scope, about not creating the skupper namespace, and deploy the controller in the namespace that the user has configured?

Makes sense to me.

Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index b442e31f..280143e2 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -3,6 +3,7 @@ orbs:
     docker: circleci/docker@1.0.1
     kube-orb: circleci/kubernetes@0.11.0
     go: circleci/go@1.7.1
+    helm: circleci/helm@3.1.0
 
 executors:
   go_cimg:
@@ -203,6 +204,22 @@ jobs:
           paths:
             - skupper-manifest
 
+  generate-skupper-setup:
+    executor:
+      name: go_cimg
+    steps:
+      - checkout
+      - go/mod-download-cached
+      - helm/install
+      - run: make generate-skupper-deployment-cluster-scoped
+      - run: make generate-skupper-deployment-namespace-scoped
+      - run: mkdir skupper-setup
+      - run: cp ./skupper-setup-*-scope.yaml skupper-setup
+      - persist_to_workspace:
+          root: .
+          paths:
+            - skupper-setup
+
   build-oci-images:
     executor: local_cluster_test_executor
     parameters:
@@ -341,6 +358,7 @@ jobs:
             done
             cd ${BASEDIR}
             cp "${BASEDIR}/skupper-manifest/manifest.json" "${BASEDIR}/archives"
+            cp "${BASEDIR}/skupper-setup/*.yaml" "${BASEDIR}/archives"
             ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -replace -prerelease -draft ${VERSION} "${BASEDIR}/archives"
 
   publish-github-prerelease-artifacts:
diff --git a/.gitignore b/.gitignore
index 88d33a1c..31adc73b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,3 +30,5 @@ vendor/
 # Generated files
 manifest.json
 oci-archives/
+skupper-setup-cluster-scope.yaml
+skupper-setup-namespace-scope.yaml
diff --git a/Makefile b/Makefile
index 181d68d8..d2ba627e 100644
--- a/Makefile
+++ b/Makefile
@@ -119,6 +119,12 @@ generate-doc: build-doc-generator
 update-helm-crd:
 	./scripts/update-helm-crds.sh
 
+generate-skupper-deployment-cluster-scoped:
+	helm template ./charts/skupper-setup  --include-crds --set scope=cluster > skupper-setup-cluster-scope.yaml
+
+generate-skupper-deployment-namespace-scoped:
+	helm template ./charts/skupper-setup  --include-crds --set scope=namespace > skupper-setup-namespace-scope.yaml
+
 clean:
 	rm -rf skupper controller kube-adaptor \
 		network-observer generate-doc \
diff --git a/deployments/helm/Chart.yaml b/charts/skupper-setup/Chart.yaml
similarity index 66%
rename from deployments/helm/Chart.yaml
rename to charts/skupper-setup/Chart.yaml
index 31e77b1b..767500d3 100644
--- a/deployments/helm/Chart.yaml
+++ b/charts/skupper-setup/Chart.yaml
@@ -1,4 +1,5 @@
 apiVersion: v2
 name: skupper-setup
 description:  Helm chart for setting up Skupper.
-version: 0.1.0
\ No newline at end of file
+version: 0.1.0-devel
+appVersion: v2-latest
\ No newline at end of file
diff --git a/deployments/helm/README.md b/charts/skupper-setup/README.md
similarity index 100%
rename from deployments/helm/README.md
rename to charts/skupper-setup/README.md
diff --git a/deployments/helm/crds/skupper_access_grant_crd.yaml b/charts/skupper-setup/crds/skupper_access_grant_crd.yaml
similarity index 100%
rename from deployments/helm/crds/skupper_access_grant_crd.yaml
rename to charts/skupper-setup/crds/skupper_access_grant_crd.yaml
diff --git a/deployments/helm/crds/skupper_access_token_crd.yaml b/charts/skupper-setup/crds/skupper_access_token_crd.yaml
similarity index 100%
rename from deployments/helm/crds/skupper_access_token_crd.yaml
rename to charts/skupper-setup/crds/skupper_access_token_crd.yaml
diff --git a/deployments/helm/crds/skupper_attached_connector_binding_crd.yaml b/charts/skupper-setup/crds/skupper_attached_connector_binding_crd.yaml
similarity index 100%
rename from deployments/helm/crds/skupper_attached_connector_binding_crd.yaml
rename to charts/skupper-setup/crds/skupper_attached_connector_binding_crd.yaml
diff --git a/deployments/helm/crds/skupper_attached_connector_crd.yaml b/charts/skupper-setup/crds/skupper_attached_connector_crd.yaml
similarity index 100%
rename from deployments/helm/crds/skupper_attached_connector_crd.yaml
rename to charts/skupper-setup/crds/skupper_attached_connector_crd.yaml
diff --git a/deployments/helm/crds/skupper_certificate_crd.yaml b/charts/skupper-setup/crds/skupper_certificate_crd.yaml
similarity index 100%
rename from deployments/helm/crds/skupper_certificate_crd.yaml
rename to charts/skupper-setup/crds/skupper_certificate_crd.yaml
diff --git a/deployments/helm/crds/skupper_connector_crd.yaml b/charts/skupper-setup/crds/skupper_connector_crd.yaml
similarity index 100%
rename from deployments/helm/crds/skupper_connector_crd.yaml
rename to charts/skupper-setup/crds/skupper_connector_crd.yaml
diff --git a/deployments/helm/crds/skupper_link_crd.yaml b/charts/skupper-setup/crds/skupper_link_crd.yaml
similarity index 100%
rename from deployments/helm/crds/skupper_link_crd.yaml
rename to charts/skupper-setup/crds/skupper_link_crd.yaml
diff --git a/deployments/helm/crds/skupper_listener_crd.yaml b/charts/skupper-setup/crds/skupper_listener_crd.yaml
similarity index 100%
rename from deployments/helm/crds/skupper_listener_crd.yaml
rename to charts/skupper-setup/crds/skupper_listener_crd.yaml
diff --git a/deployments/helm/crds/skupper_router_access_crd.yaml b/charts/skupper-setup/crds/skupper_router_access_crd.yaml
similarity index 100%
rename from deployments/helm/crds/skupper_router_access_crd.yaml
rename to charts/skupper-setup/crds/skupper_router_access_crd.yaml
diff --git a/deployments/helm/crds/skupper_secured_access_crd.yaml b/charts/skupper-setup/crds/skupper_secured_access_crd.yaml
similarity index 100%
rename from deployments/helm/crds/skupper_secured_access_crd.yaml
rename to charts/skupper-setup/crds/skupper_secured_access_crd.yaml
diff --git a/deployments/helm/crds/skupper_site_crd.yaml b/charts/skupper-setup/crds/skupper_site_crd.yaml
similarity index 100%
rename from deployments/helm/crds/skupper_site_crd.yaml
rename to charts/skupper-setup/crds/skupper_site_crd.yaml
diff --git a/deployments/helm/templates/deploy_controller.yaml b/charts/skupper-setup/templates/deploy_controller.yaml
similarity index 100%
rename from deployments/helm/templates/deploy_controller.yaml
rename to charts/skupper-setup/templates/deploy_controller.yaml
diff --git a/deployments/helm/values.yaml b/charts/skupper-setup/values.yaml
similarity index 100%
rename from deployments/helm/values.yaml
rename to charts/skupper-setup/values.yaml
diff --git a/cmd/controller/deploy_cluster_scope.yaml b/cmd/controller/deploy_cluster_scope.yaml
index aef4ab13..e69de29b 100644
--- a/cmd/controller/deploy_cluster_scope.yaml
+++ b/cmd/controller/deploy_cluster_scope.yaml
@@ -1,234 +0,0 @@
----
-apiVersion: v1
-kind: Namespace
-metadata:
-  name: skupper
----
-apiVersion: v1
-kind: ServiceAccount
-metadata:
-  name: skupper-controller
-  namespace: skupper
-  labels:
-    application: skupper-controller
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  labels:
-    application: skupper-controller
-  name: skupper-controller
-rules:
-- apiGroups:
-  - ""
-  resources:
-  - nodes
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - ""
-  resources:
-  - configmaps
-  - pods
-  - pods/exec
-  - services
-  - secrets
-  - serviceaccounts
-  - events
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - delete
-  - patch
-- apiGroups:
-  - apps
-  resources:
-  - deployments
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - delete
-  - patch
-- apiGroups:
-  - route.openshift.io
-  resources:
-  - routes
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - delete
-  - update
-- apiGroups:
-    - apps.openshift.io
-  resources:
-    - deploymentconfigs
-  verbs:
-    - get
-    - list
-    - watch
-- apiGroups:
-  - networking.k8s.io
-  resources:
-  - ingresses
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - delete
-  - update
-- apiGroups:
-  - projectcontour.io
-  resources:
-  - httpproxies
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - delete
-  - update
-- apiGroups:
-  - gateway.networking.k8s.io
-  resources:
-  - gateways
-  - tlsroutes
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - delete
-  - update
-  - patch
-- apiGroups:
-  - coordination.k8s.io
-  resources:
-  - leases
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - delete
-  - update
-- apiGroups:
-  - rbac.authorization.k8s.io
-  resources:
-  - rolebindings
-  - roles
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - delete
-  - update
-- apiGroups:
-  - skupper.io
-  resources:
-  - sites
-  - sites/status
-  - links
-  - links/status
-  - accesstokens
-  - accesstokens/status
-  - accessgrants
-  - accessgrants/status
-  - listeners
-  - listeners/status
-  - connectors
-  - connectors/status
-  - attachedconnectors
-  - attachedconnectors/status
-  - attachedconnectorbindings
-  - attachedconnectorbindings/status
-  - routeraccesses
-  - routeraccesses/status
-  - securedaccesses
-  - securedaccesses/status
-  - certificates
-  - certificates/status
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - delete
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
-  labels:
-    application: skupper-controller
-  name: skupper-controller
-subjects:
-- kind: ServiceAccount
-  name: skupper-controller
-  namespace: skupper
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: skupper-controller
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: skupper-controller
-  namespace: skupper
-spec:
-  replicas: 1
-  selector:
-    matchLabels:
-      application: skupper-controller
-  template:
-    metadata:
-      labels:
-        app.kubernetes.io/part-of: skupper
-        application: skupper-controller
-    spec:
-      serviceAccountName: skupper-controller
-      # Prevent kubernetes from injecting env vars for grant service
-      # as these then collide with those that actually configure the
-      # controller:
-      enableServiceLinks: false
-      # Please ensure that you can use SeccompProfile and do not use
-      # if your project must work on old Kubernetes
-      # versions < 1.19 or on vendors versions which
-      # do NOT support this field by default
-      securityContext:
-        runAsNonRoot: true
-        seccompProfile:
-          type: RuntimeDefault
-      containers:
-      - name: controller
-        image: quay.io/skupper/controller:v2-latest
-        imagePullPolicy: Always
-        command: ["/app/controller"]
-        args: ["-enable-grants", "-grant-server-autoconfigure"]
-        env:
-        - name: SKUPPER_ADAPTOR_IMAGE
-          value: quay.io/skupper/kube-adaptor:v2-latest
-        securityContext:
-          capabilities:
-            drop:
-            - ALL
-          runAsNonRoot: true
-          allowPrivilegeEscalation: false
-        volumeMounts:
-        - name: tls-credentials
-          mountPath: /etc/controller
-      volumes:
-      - name: tls-credentials
-        emptyDir: {}
diff --git a/cmd/controller/deploy_namespace_scope.yaml b/cmd/controller/deploy_namespace_scope.yaml
index f8d238cc..e69de29b 100644
--- a/cmd/controller/deploy_namespace_scope.yaml
+++ b/cmd/controller/deploy_namespace_scope.yaml
@@ -1,226 +0,0 @@
----
-apiVersion: v1
-kind: ServiceAccount
-metadata:
-  name: skupper-controller
-  labels:
-    application: skupper-controller
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: Role
-metadata:
-  labels:
-    application: skupper-controller
-  name: skupper-controller
-rules:
-- apiGroups:
-  - ""
-  resources:
-  - nodes
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - ""
-  resources:
-  - configmaps
-  - pods
-  - pods/exec
-  - services
-  - secrets
-  - serviceaccounts
-  - events
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - delete
-  - patch
-- apiGroups:
-  - apps
-  resources:
-  - deployments
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - delete
-  - patch
-- apiGroups:
-  - route.openshift.io
-  resources:
-  - routes
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - delete
-  - update
-- apiGroups:
-    - apps.openshift.io
-  resources:
-    - deploymentconfigs
-  verbs:
-    - get
-    - list
-    - watch
-- apiGroups:
-  - networking.k8s.io
-  resources:
-  - ingresses
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - delete
-  - update
-- apiGroups:
-  - projectcontour.io
-  resources:
-  - httpproxies
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - delete
-  - update
-- apiGroups:
-  - gateway.networking.k8s.io
-  resources:
-  - gateways
-  - tlsroutes
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - delete
-  - update
-  - patch
-- apiGroups:
-  - coordination.k8s.io
-  resources:
-  - leases
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - delete
-  - update
-- apiGroups:
-  - rbac.authorization.k8s.io
-  resources:
-  - rolebindings
-  - roles
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - delete
-  - update
-- apiGroups:
-  - skupper.io
-  resources:
-  - sites
-  - sites/status
-  - links
-  - links/status
-  - accesstokens
-  - accesstokens/status
-  - accessgrants
-  - accessgrants/status
-  - listeners
-  - listeners/status
-  - connectors
-  - connectors/status
-  - attachedconnectors
-  - attachedconnectors/status
-  - attachedconnectorbindings
-  - attachedconnectorbindings/status
-  - routeraccesses
-  - routeraccesses/status
-  - securedaccesses
-  - securedaccesses/status
-  - certificates
-  - certificates/status
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - delete
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: RoleBinding
-metadata:
-  labels:
-    application: skupper-controller
-  name: skupper-controller
-subjects:
-- kind: ServiceAccount
-  name: skupper-controller
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: Role
-  name: skupper-controller
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: skupper-controller
-spec:
-  replicas: 1
-  selector:
-    matchLabels:
-      application: skupper-controller
-  template:
-    metadata:
-      labels:
-        app.kubernetes.io/part-of: skupper
-        application: skupper-controller
-    spec:
-      serviceAccountName: skupper-controller
-      # Prevent kubernetes from injecting env vars for grant service
-      # as these then collide with those that actually configure the
-      # controller:
-      enableServiceLinks: false
-      securityContext:
-        runAsNonRoot: true
-        seccompProfile:
-          type: RuntimeDefault
-      containers:
-      - name: controller
-        image: quay.io/skupper/controller:v2-latest
-        imagePullPolicy: Always
-        command: ["/app/controller"]
-        args: ["-enable-grants", "-grant-server-autoconfigure"]
-        env:
-        - name: SKUPPER_ADAPTOR_IMAGE
-          value: quay.io/skupper/kube-adaptor:v2-latest
-        - name: WATCH_NAMESPACE
-          valueFrom:
-             fieldRef:
-               fieldPath: metadata.namespace
-        securityContext:
-          capabilities:
-            drop:
-            - ALL
-          runAsNonRoot: true
-          allowPrivilegeEscalation: false
-        volumeMounts:
-        - name: tls-credentials
-          mountPath: /etc/controller
-      volumes:
-      - name: tls-credentials
-        emptyDir: {}
diff --git a/cmd/network-observer/internal/api/types_gen.go b/cmd/network-observer/internal/api/types_gen.go
index 0a890a81..2c0197bb 100644
--- a/cmd/network-observer/internal/api/types_gen.go
+++ b/cmd/network-observer/internal/api/types_gen.go
@@ -114,6 +114,8 @@ type ApplicationFlowRecord struct {
 	// Identity The unique identifier for the record.
 	Identity          string `json:"identity"`
 	Method            string `json:"method"`
+	Octets            uint64 `json:"octets"`
+	OctetsReverse     uint64 `json:"octetsReverse"`
 	Protocol          string `json:"protocol"`
 	RoutingKey        string `json:"routingKey"`
 	SourceProcessId   string `json:"sourceProcessId"`
diff --git a/cmd/network-observer/internal/server/views/flows.go b/cmd/network-observer/internal/server/views/flows.go
index f8ce4fef..1098b60f 100644
--- a/cmd/network-observer/internal/server/views/flows.go
+++ b/cmd/network-observer/internal/server/views/flows.go
@@ -26,6 +26,7 @@ func NewConnectionsSliceProvider(stor store.Interface) func([]store.Entry) []api
 		return results
 	}
 }
+
 func NewConnectionsProvider(stor store.Interface) func(collector.ConnectionRecord) (api.ConnectionRecord, bool) {
 	traceProvider := newTraceProvider(stor)
 	return func(conn collector.ConnectionRecord) (api.ConnectionRecord, bool) {
@@ -199,6 +200,8 @@ func NewRequestProvider(stor store.Interface) func(collector.RequestRecord) (api
 		out.StartTime, out.EndTime = vanflowTimes(record.BaseRecord)
 		setOpt(&out.Method, record.Method)
 		setOpt(&out.Status, record.Result)
+		setOpt(&out.Octets, record.Octets)
+		setOpt(&out.OctetsReverse, record.OctetsReverse)
 
 		if record.EndTime != nil && record.StartTime != nil && record.EndTime.After(record.StartTime.Time) {
 			if record.EndTime != nil && record.StartTime != nil {
diff --git a/cmd/network-observer/spec/openapi.yaml b/cmd/network-observer/spec/openapi.yaml
index 4f657497..dfb35d1b 100644
--- a/cmd/network-observer/spec/openapi.yaml
+++ b/cmd/network-observer/spec/openapi.yaml
@@ -1378,6 +1378,8 @@ components:
             - status
             - traceRouters
             - traceSites
+            - octets
+            - octetsReverse
           properties:
             connectionId:
               type: string
@@ -1412,6 +1414,12 @@ components:
             status:
               type: string
               example: 200
+            octets:
+              type: integer
+              format: uint64
+            octetsReverse:
+              type: integer
+              format: uint64
             traceRouters:
               type: array
               description: Ordered array of the names of routers involved in proxying the connection
diff --git a/cmd/skupper/README.md b/cmd/skupper/README.md
index 9fdcf93c..1392cdd2 100644
--- a/cmd/skupper/README.md
+++ b/cmd/skupper/README.md
@@ -5,26 +5,8 @@ kubectl create namespace skupper
 kubectl config set-context --current --namespace skupper
 ```
 
-Apply the CRDs in the cluster:
+Apply the CRDs in the cluster and deploy the skupper controller ([instructions](../../charts/skupper-setup/README.md)):
 
-```
-kubectl apply -f https://raw.githubusercontent.com/skupperproject/skupper/v2/api/types/crds/skupper_access_grant_crd.yaml
-kubectl apply -f https://raw.githubusercontent.com/skupperproject/skupper/v2/api/types/crds/skupper_access_token_crd.yaml
-kubectl apply -f https://raw.githubusercontent.com/skupperproject/skupper/v2/api/types/crds/skupper_attached_connector_anchor_crd.yaml
-kubectl apply -f https://raw.githubusercontent.com/skupperproject/skupper/v2/api/types/crds/skupper_attached_connector_crd.yaml
-kubectl apply -f https://raw.githubusercontent.com/skupperproject/skupper/v2/api/types/crds/skupper_certificate_crd.yaml
-kubectl apply -f https://raw.githubusercontent.com/skupperproject/skupper/v2/api/types/crds/skupper_connector_crd.yaml
-kubectl apply -f https://raw.githubusercontent.com/skupperproject/skupper/v2/api/types/crds/skupper_link_crd.yaml
-kubectl apply -f https://raw.githubusercontent.com/skupperproject/skupper/v2/api/types/crds/skupper_listener_crd.yaml
-kubectl apply -f https://raw.githubusercontent.com/skupperproject/skupper/v2/api/types/crds/skupper_router_access_crd.yaml
-kubectl apply -f https://raw.githubusercontent.com/skupperproject/skupper/v2/api/types/crds/skupper_secured_access_crd.yaml
-kubectl apply -f https://raw.githubusercontent.com/skupperproject/skupper/v2/api/types/crds/skupper_site_crd.yaml
-```
-
-Deploy the Skupper controller in the cluster:
-```
-kubectl apply -f https://raw.githubusercontent.com/skupperproject/skupper/v2/cmd/controller/deploy_cluster_scope.yaml
-```
 
 # Deploy application in two namespaces (or contexts)
 
diff --git a/internal/cmd/skupper/common/flags.go b/internal/cmd/skupper/common/flags.go
index 5c8bcbd0..383ed2b4 100644
--- a/internal/cmd/skupper/common/flags.go
+++ b/internal/cmd/skupper/common/flags.go
@@ -82,6 +82,10 @@ for other Kubernetes flavors, loadbalancer is the default.`
 	FlagNameWait       = "wait"
 	FlagDescWait       = "Wait for the given status before exiting. Choices: configured, ready, none"
 	FlagDescDeleteWait = "Wait for deletion to complete before exiting"
+
+	FlagNameAll       = "all"
+	FlagDescAll       = "delete all skupper resources in current namespace"
+	FlagDescDeleteAll = "delete all skupper resources associated with site in current namespace"
 )
 
 type CommandSiteCreateFlags struct {
@@ -109,6 +113,7 @@ type CommandSiteUpdateFlags struct {
 }
 
 type CommandSiteDeleteFlags struct {
+	All     bool
 	Timeout time.Duration
 	Wait    bool
 }
@@ -141,6 +146,7 @@ type CommandTokenIssueFlags struct {
 	Timeout            time.Duration
 	ExpirationWindow   time.Duration
 	RedemptionsAllowed int
+	Cost               string
 }
 
 type CommandTokenRedeemFlags struct {
diff --git a/internal/cmd/skupper/site/kube/site_delete.go b/internal/cmd/skupper/site/kube/site_delete.go
index 1779161d..6d731770 100644
--- a/internal/cmd/skupper/site/kube/site_delete.go
+++ b/internal/cmd/skupper/site/kube/site_delete.go
@@ -3,10 +3,11 @@ package kube
 import (
 	"context"
 	"fmt"
+	"time"
+
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"github.com/skupperproject/skupper/pkg/utils/validator"
-	"time"
 
 	"github.com/skupperproject/skupper/internal/kube/client"
 	skupperv2alpha1 "github.com/skupperproject/skupper/pkg/generated/client/clientset/versioned/typed/skupper/v2alpha1"
@@ -22,6 +23,7 @@ type CmdSiteDelete struct {
 	siteName  string
 	timeout   time.Duration
 	wait      bool
+	all       bool
 }
 
 func NewCmdSiteDelete() *CmdSiteDelete {
@@ -87,11 +89,120 @@ func (cmd *CmdSiteDelete) ValidateInput(args []string) []error {
 func (cmd *CmdSiteDelete) InputToOptions() {
 	cmd.timeout = cmd.Flags.Timeout
 	cmd.wait = cmd.Flags.Wait
+	cmd.all = cmd.Flags.All
 }
 
 func (cmd *CmdSiteDelete) Run() error {
+
 	err := cmd.Client.Sites(cmd.Namespace).Delete(context.TODO(), cmd.siteName, metav1.DeleteOptions{})
-	return err
+	if err != nil {
+		return err
+	}
+
+	// if delete all, also remove all the other resources
+	if cmd.all {
+		connectors, err := cmd.Client.Connectors(cmd.Namespace).List(context.TODO(), metav1.ListOptions{})
+		if err == nil && connectors != nil && len(connectors.Items) != 0 {
+			for _, connector := range connectors.Items {
+				err = cmd.Client.Connectors(cmd.Namespace).Delete(context.TODO(), connector.Name, metav1.DeleteOptions{})
+				if err != nil {
+					return err
+				}
+			}
+		}
+
+		listeners, err := cmd.Client.Listeners(cmd.Namespace).List(context.TODO(), metav1.ListOptions{})
+		if err == nil && listeners != nil && len(listeners.Items) != 0 {
+			for _, listener := range listeners.Items {
+				err = cmd.Client.Listeners(cmd.Namespace).Delete(context.TODO(), listener.Name, metav1.DeleteOptions{})
+				if err != nil {
+					return err
+				}
+			}
+		}
+
+		links, err := cmd.Client.Links(cmd.Namespace).List(context.TODO(), metav1.ListOptions{})
+		if err == nil && links != nil && len(links.Items) != 0 {
+			for _, link := range links.Items {
+				err = cmd.Client.Links(cmd.Namespace).Delete(context.TODO(), link.Name, metav1.DeleteOptions{})
+				if err != nil {
+					return err
+				}
+			}
+		}
+
+		accessTokens, err := cmd.Client.AccessTokens(cmd.Namespace).List(context.TODO(), metav1.ListOptions{})
+		if err == nil && accessTokens != nil && len(accessTokens.Items) != 0 {
+			for _, accessToken := range accessTokens.Items {
+				err = cmd.Client.AccessTokens(cmd.Namespace).Delete(context.TODO(), accessToken.Name, metav1.DeleteOptions{})
+				if err != nil {
+					return err
+				}
+			}
+		}
+
+		accessGrants, err := cmd.Client.AccessGrants(cmd.Namespace).List(context.TODO(), metav1.ListOptions{})
+		if err == nil && accessGrants != nil && len(accessGrants.Items) != 0 {
+			for _, accessGrant := range accessGrants.Items {
+				err = cmd.Client.AccessGrants(cmd.Namespace).Delete(context.TODO(), accessGrant.Name, metav1.DeleteOptions{})
+				if err != nil {
+					return err
+				}
+			}
+		}
+
+		routerAccesses, err := cmd.Client.RouterAccesses(cmd.Namespace).List(context.TODO(), metav1.ListOptions{})
+		if err == nil && routerAccesses != nil && len(routerAccesses.Items) != 0 {
+			for _, routerAccess := range routerAccesses.Items {
+				err = cmd.Client.RouterAccesses(cmd.Namespace).Delete(context.TODO(), routerAccess.Name, metav1.DeleteOptions{})
+				if err != nil {
+					return err
+				}
+			}
+		}
+
+		securedAccesses, err := cmd.Client.SecuredAccesses(cmd.Namespace).List(context.TODO(), metav1.ListOptions{})
+		if err == nil && securedAccesses != nil && len(securedAccesses.Items) != 0 {
+			for _, securedAccess := range securedAccesses.Items {
+				err = cmd.Client.SecuredAccesses(cmd.Namespace).Delete(context.TODO(), securedAccess.Name, metav1.DeleteOptions{})
+				if err != nil {
+					return err
+				}
+			}
+		}
+
+		certificates, err := cmd.Client.Certificates(cmd.Namespace).List(context.TODO(), metav1.ListOptions{})
+		if err == nil && certificates != nil && len(certificates.Items) != 0 {
+			for _, certificate := range certificates.Items {
+				err = cmd.Client.Certificates(cmd.Namespace).Delete(context.TODO(), certificate.Name, metav1.DeleteOptions{})
+				if err != nil {
+					return err
+				}
+			}
+		}
+
+		attachedConnectors, err := cmd.Client.AttachedConnectors(cmd.Namespace).List(context.TODO(), metav1.ListOptions{})
+		if err == nil && attachedConnectors != nil && len(attachedConnectors.Items) != 0 {
+			for _, attachedConnector := range attachedConnectors.Items {
+				err = cmd.Client.AttachedConnectors(cmd.Namespace).Delete(context.TODO(), attachedConnector.Name, metav1.DeleteOptions{})
+				if err != nil {
+					return err
+				}
+			}
+		}
+
+		attachedConnectorBindings, err := cmd.Client.AttachedConnectorBindings(cmd.Namespace).List(context.TODO(), metav1.ListOptions{})
+		if err == nil && attachedConnectorBindings != nil && len(attachedConnectorBindings.Items) != 0 {
+			for _, attachedConnectorBinding := range attachedConnectorBindings.Items {
+				err = cmd.Client.AttachedConnectorBindings(cmd.Namespace).Delete(context.TODO(), attachedConnectorBinding.Name, metav1.DeleteOptions{})
+				if err != nil {
+					return err
+				}
+			}
+		}
+	}
+
+	return nil
 }
 func (cmd *CmdSiteDelete) WaitUntil() error {
 
diff --git a/internal/cmd/skupper/site/site.go b/internal/cmd/skupper/site/site.go
index 8b5c2549..5c565219 100644
--- a/internal/cmd/skupper/site/site.go
+++ b/internal/cmd/skupper/site/site.go
@@ -148,6 +148,7 @@ skupper site delete --wait=false`,
 	cmd := common.ConfigureCobraCommand(configuredPlatform, cmdSiteDeleteDesc, kubeCommand, nonKubeCommand)
 	cmdFlags := common.CommandSiteDeleteFlags{}
 
+	cmd.Flags().BoolVar(&cmdFlags.All, common.FlagNameAll, false, common.FlagDescDeleteAll)
 	cmd.Flags().DurationVar(&cmdFlags.Timeout, common.FlagNameTimeout, 60*time.Second, common.FlagDescTimeout)
 	cmd.Flags().BoolVar(&cmdFlags.Wait, common.FlagNameWait, true, common.FlagDescDeleteWait)
 
diff --git a/internal/cmd/skupper/token/kube/token_issue.go b/internal/cmd/skupper/token/kube/token_issue.go
index d6a0873e..9283cfa1 100644
--- a/internal/cmd/skupper/token/kube/token_issue.go
+++ b/internal/cmd/skupper/token/kube/token_issue.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"fmt"
 	"os"
+	"strconv"
 
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
@@ -25,6 +26,7 @@ type CmdTokenIssue struct {
 	namespace string
 	grantName string
 	fileName  string
+	cost      int
 }
 
 func NewCmdTokenIssue() *CmdTokenIssue {
@@ -47,6 +49,7 @@ func (cmd *CmdTokenIssue) ValidateInput(args []string) []error {
 	tokenStringValidator := validator.NewFilePathStringValidator()
 	timeoutValidator := validator.NewTimeoutInSecondsValidator()
 	expirationValidator := validator.NewExpirationInSecondsValidator()
+	numberValidator := validator.NewNumberValidator()
 
 	// Validate token file name
 	if len(args) < 1 {
@@ -114,6 +117,17 @@ func (cmd *CmdTokenIssue) ValidateInput(args []string) []error {
 		}
 	}
 
+	selectedCost, err := strconv.Atoi(cmd.Flags.Cost)
+	if err != nil {
+		validationErrors = append(validationErrors, fmt.Errorf("link cost is not valid: %s", err))
+	}
+	ok, err := numberValidator.Evaluate(selectedCost)
+	if !ok {
+		validationErrors = append(validationErrors, fmt.Errorf("link cost is not valid: %s", err))
+	} else {
+		cmd.cost = selectedCost
+	}
+
 	return validationErrors
 }
 
@@ -157,9 +171,10 @@ func (cmd *CmdTokenIssue) WaitUntil() error {
 					Name: accessGrant.Name,
 				},
 				Spec: v2alpha1.AccessTokenSpec{
-					Url:  accessGrant.Status.Url,
-					Code: accessGrant.Status.Code,
-					Ca:   accessGrant.Status.Ca,
+					Url:      accessGrant.Status.Url,
+					Code:     accessGrant.Status.Code,
+					Ca:       accessGrant.Status.Ca,
+					LinkCost: cmd.cost,
 				},
 			}
 
diff --git a/internal/cmd/skupper/token/token.go b/internal/cmd/skupper/token/token.go
index 274c3dc3..35956b2c 100644
--- a/internal/cmd/skupper/token/token.go
+++ b/internal/cmd/skupper/token/token.go
@@ -49,6 +49,7 @@ func CmdTokenIssueFactory(configuredPlatform types.Platform) *cobra.Command {
 	cmd.Flags().DurationVarP(&cmdFlags.ExpirationWindow, common.FlagNameExpirationWindow, "e", 15*time.Minute, common.FlagDescExpirationWindow)
 	cmd.Flags().DurationVarP(&cmdFlags.Timeout, common.FlagNameTimeout, "t", 60*time.Second, common.FlagDescTimeout)
 	cmd.Flags().StringVar(&cmdFlags.Name, common.FlagNameToken, "", common.FlagDescToken)
+	cmd.Flags().StringVar(&cmdFlags.Cost, common.FlagNameCost, "1", common.FlagDescCost)
 
 	kubeCommand.CobraCmd = cmd
 	kubeCommand.Flags = &cmdFlags
diff --git a/pkg/vanflow/records.go b/pkg/vanflow/records.go
index 3fc56696..e0f33188 100644
--- a/pkg/vanflow/records.go
+++ b/pkg/vanflow/records.go
@@ -292,11 +292,13 @@ func (r TransportBiflowRecord) GetTypeMeta() TypeMeta {
 
 type AppBiflowRecord struct {
 	BaseRecord
-	Parent   *string `vflow:"2"`
-	Protocol *string `vflow:"16"`
-	Latency  *uint64 `vflow:"24"`
-	Method   *string `vflow:"27"`
-	Result   *string `vflow:"28"`
+	Parent        *string `vflow:"2"`
+	Protocol      *string `vflow:"16"`
+	Latency       *uint64 `vflow:"24"`
+	Method        *string `vflow:"27"`
+	Result        *string `vflow:"28"`
+	Octets        *uint64 `vflow:"23"`
+	OctetsReverse *uint64 `vflow:"58"`
 }
 
 func (r AppBiflowRecord) GetTypeMeta() TypeMeta {


Test patch:
diff --git a/internal/cmd/skupper/site/kube/site_delete_test.go b/internal/cmd/skupper/site/kube/site_delete_test.go
index 6006e550..6b87e055 100644
--- a/internal/cmd/skupper/site/kube/site_delete_test.go
+++ b/internal/cmd/skupper/site/kube/site_delete_test.go
@@ -1,11 +1,12 @@
 package kube
 
 import (
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"testing"
 	"time"
 
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
+
 	fakeclient "github.com/skupperproject/skupper/internal/kube/client/fake"
 
 	"github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
@@ -251,6 +252,7 @@ func TestCmdSiteDelete_InputToOptions(t *testing.T) {
 		flags           *common.CommandSiteDeleteFlags
 		expectedTimeout time.Duration
 		expectedWait    bool
+		expectedAll     bool
 	}
 
 	testTable := []test{
@@ -264,6 +266,11 @@ func TestCmdSiteDelete_InputToOptions(t *testing.T) {
 			flags:        &common.CommandSiteDeleteFlags{Wait: true},
 			expectedWait: true,
 		},
+		{
+			name:        "all site resources",
+			flags:       &common.CommandSiteDeleteFlags{All: true},
+			expectedAll: true,
+		},
 	}
 
 	for _, test := range testTable {
@@ -276,6 +283,7 @@ func TestCmdSiteDelete_InputToOptions(t *testing.T) {
 
 			assert.Check(t, command.timeout == test.expectedTimeout)
 			assert.Check(t, command.wait == test.expectedWait)
+			assert.Check(t, command.all == test.expectedAll)
 		})
 	}
 }
@@ -288,6 +296,7 @@ func TestCmdSiteDelete_Run(t *testing.T) {
 		skupperError   string
 		siteName       string
 		errorMessage   string
+		all            bool
 	}
 
 	testTable := []test{
@@ -327,6 +336,101 @@ func TestCmdSiteDelete_Run(t *testing.T) {
 			siteName:       "my-site",
 			errorMessage:   "sites.skupper.io \"my-site\" not found",
 		},
+		{
+			name:       "deletes all",
+			all:        true,
+			k8sObjects: nil,
+			skupperObjects: []runtime.Object{
+				&v2alpha1.Site{
+					ObjectMeta: v1.ObjectMeta{
+						Name:      "my-site",
+						Namespace: "test",
+					},
+					Status: v2alpha1.SiteStatus{
+						Status: v2alpha1.Status{
+							Message: "OK",
+							Conditions: []v1.Condition{
+								{
+									Message:            "OK",
+									ObservedGeneration: 1,
+									Reason:             "OK",
+									Status:             "True",
+									Type:               "Configured",
+								},
+							},
+						},
+					},
+				},
+				&v2alpha1.AccessGrant{
+					ObjectMeta: v1.ObjectMeta{
+						Name:      "my-token",
+						Namespace: "test",
+					},
+				},
+				&v2alpha1.AccessToken{
+					ObjectMeta: v1.ObjectMeta{
+						Name:      "my-token",
+						Namespace: "test",
+					},
+				},
+				&v2alpha1.Connector{
+					ObjectMeta: v1.ObjectMeta{
+						Name:      "my-connector",
+						Namespace: "test",
+					},
+				},
+				&v2alpha1.Listener{
+					ObjectMeta: v1.ObjectMeta{
+						Name:      "my-listener",
+						Namespace: "test",
+					},
+				},
+				&v2alpha1.Site{
+					ObjectMeta: v1.ObjectMeta{
+						Name:      "the-site",
+						Namespace: "test",
+					},
+				},
+				&v2alpha1.Certificate{
+					ObjectMeta: v1.ObjectMeta{
+						Name:      "link-test",
+						Namespace: "test",
+					},
+				},
+				&v2alpha1.Link{
+					ObjectMeta: v1.ObjectMeta{
+						Name:      "my-link",
+						Namespace: "test",
+					},
+				},
+				&v2alpha1.AttachedConnectorBinding{
+					ObjectMeta: v1.ObjectMeta{
+						Name:      "my-attachedConnectorBinding",
+						Namespace: "test",
+					},
+				},
+				&v2alpha1.AttachedConnector{
+					ObjectMeta: v1.ObjectMeta{
+						Name:      "my-attachedConnector",
+						Namespace: "test",
+					},
+				},
+				&v2alpha1.RouterAccess{
+					ObjectMeta: v1.ObjectMeta{
+						Name:      "my-routerAccess",
+						Namespace: "test",
+					},
+				},
+				&v2alpha1.SecuredAccess{
+					ObjectMeta: v1.ObjectMeta{
+						Name:      "my-securedAccess",
+						Namespace: "test",
+					},
+				},
+			},
+			skupperError: "",
+			siteName:     "my-site",
+		},
 	}
 
 	for _, test := range testTable {
@@ -337,7 +441,7 @@ func TestCmdSiteDelete_Run(t *testing.T) {
 		fakeSkupperClient, err := fakeclient.NewFakeClient(command.Namespace, test.k8sObjects, test.skupperObjects, test.skupperError)
 		assert.Assert(t, err)
 		command.Client = fakeSkupperClient.GetSkupperClient().SkupperV2alpha1()
-
+		command.all = test.all
 		command.siteName = test.siteName
 
 		t.Run(test.name, func(t *testing.T) {
diff --git a/internal/cmd/skupper/site/site_test.go b/internal/cmd/skupper/site/site_test.go
index 53339e1a..9cbe32d2 100644
--- a/internal/cmd/skupper/site/site_test.go
+++ b/internal/cmd/skupper/site/site_test.go
@@ -1,12 +1,13 @@
 package site
 
 import (
+	"testing"
+
 	"github.com/skupperproject/skupper/api/types"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/spf13/cobra"
 	"github.com/spf13/pflag"
 	"gotest.tools/v3/assert"
-	"testing"
 )
 
 func TestCmdSiteFactory(t *testing.T) {
@@ -51,6 +52,7 @@ func TestCmdSiteFactory(t *testing.T) {
 			expectedFlagsWithDefaultValue: map[string]interface{}{
 				common.FlagNameTimeout: "1m0s",
 				common.FlagNameWait:    "true",
+				common.FlagNameAll:     "false",
 			},
 			command: CmdSiteDeleteFactory(types.PlatformKubernetes),
 		},
diff --git a/internal/cmd/skupper/token/kube/token_issue_test.go b/internal/cmd/skupper/token/kube/token_issue_test.go
index f9a538b2..0502d760 100644
--- a/internal/cmd/skupper/token/kube/token_issue_test.go
+++ b/internal/cmd/skupper/token/kube/token_issue_test.go
@@ -34,6 +34,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 				RedemptionsAllowed: 1,
 				Timeout:            60 * time.Second,
 				Name:               "my-token",
+				Cost:               "1",
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.SiteList{
@@ -94,6 +95,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 				ExpirationWindow:   15 * time.Minute,
 				RedemptionsAllowed: 1,
 				Timeout:            60 * time.Second,
+				Cost:               "1",
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.AccessGrant{
@@ -122,6 +124,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 				ExpirationWindow:   15 * time.Minute,
 				RedemptionsAllowed: 1,
 				Timeout:            60 * time.Second,
+				Cost:               "1",
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.SiteList{
@@ -153,6 +156,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 				ExpirationWindow:   15 * time.Minute,
 				RedemptionsAllowed: 1,
 				Timeout:            60 * time.Second,
+				Cost:               "1",
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.SiteList{
@@ -193,6 +197,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 				ExpirationWindow:   15 * time.Minute,
 				RedemptionsAllowed: 1,
 				Timeout:            60 * time.Second,
+				Cost:               "1",
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.SiteList{
@@ -233,6 +238,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 				ExpirationWindow:   15 * time.Minute,
 				RedemptionsAllowed: 1,
 				Timeout:            60 * time.Second,
+				Cost:               "1",
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.SiteList{
@@ -274,6 +280,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 				RedemptionsAllowed: 1,
 				Timeout:            60 * time.Second,
 				Name:               "my new token",
+				Cost:               "1",
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.SiteList{
@@ -314,6 +321,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 				ExpirationWindow:   15 * time.Minute,
 				RedemptionsAllowed: 1,
 				Timeout:            60 * time.Second,
+				Cost:               "1",
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.SiteList{
@@ -354,6 +362,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 				ExpirationWindow:   15 * time.Minute,
 				RedemptionsAllowed: 0,
 				Timeout:            60 * time.Second,
+				Cost:               "1",
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.SiteList{
@@ -395,6 +404,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 				ExpirationWindow:   10 * time.Second,
 				RedemptionsAllowed: 1,
 				Timeout:            10 * time.Second,
+				Cost:               "1",
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.SiteList{
@@ -435,6 +445,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 				ExpirationWindow:   15 * time.Minute,
 				RedemptionsAllowed: 1,
 				Timeout:            0 * time.Second,
+				Cost:               "1",
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.SiteList{
@@ -468,6 +479,48 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 			},
 			expectedErrors: []string{"timeout is not valid: duration must not be less than 10s; got 0s"},
 		},
+		{
+			name: "cost is not valid",
+			args: []string{"~/token.yaml"},
+			flags: common.CommandTokenIssueFlags{
+				ExpirationWindow:   15 * time.Minute,
+				RedemptionsAllowed: 1,
+				Timeout:            60 * time.Second,
+				Cost:               "Not-valid",
+			},
+			skupperObjects: []runtime.Object{
+				&v2alpha1.SiteList{
+					Items: []v2alpha1.Site{
+						{
+							ObjectMeta: v1.ObjectMeta{
+								Name:      "site1",
+								Namespace: "test",
+							},
+							Status: v2alpha1.SiteStatus{
+								Status: v2alpha1.Status{
+									Conditions: []v1.Condition{
+										{
+											Type:   "Configured",
+											Status: "True",
+										},
+										{
+											Type:   "Running",
+											Status: "True",
+										},
+										{
+											Type:   "Ready",
+											Status: "True",
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			expectedErrors: []string{
+				`link cost is not valid: strconv.Atoi: parsing "Not-valid": invalid syntax`},
+		},
 		{
 			name: "flags all valid",
 			args: []string{"~/token.yaml"},
@@ -475,6 +528,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 				ExpirationWindow:   15 * time.Minute,
 				RedemptionsAllowed: 1,
 				Timeout:            60 * time.Second,
+				Cost:               "5",
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.SiteList{
@@ -569,6 +623,7 @@ func TestCmdTokenIssue_Run(t *testing.T) {
 				ExpirationWindow:   20 * time.Minute,
 				RedemptionsAllowed: 2,
 				Timeout:            60 * time.Second,
+				Cost:               "5",
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.AccessGrant{
diff --git a/internal/cmd/skupper/token/token_test.go b/internal/cmd/skupper/token/token_test.go
index c3608f6c..8adbd880 100644
--- a/internal/cmd/skupper/token/token_test.go
+++ b/internal/cmd/skupper/token/token_test.go
@@ -27,6 +27,7 @@ func TestCmdTokenFactory(t *testing.T) {
 				common.FlagNameExpirationWindow:   "15m0s",
 				common.FlagNameRedemptionsAllowed: "1",
 				common.FlagNameToken:              "",
+				common.FlagNameCost:               "1",
 			},
 			command: CmdTokenIssueFactory(types.PlatformKubernetes),
 		},