Repository: skupperproject/skupper
Pull request #1793 (2024-11-19 21:50:57)

[v2] Add listener unit tests to pkg/kube/site
Add unit tests for ListenerUpdated, ListenerDeleted, updateBridgeConfigForListener.

Karen-Schoener: Coverage for the new unit tests are:
```
github.com/skupperproject/skupper/pkg/kube/site/bindings.go:83:                 ListenerUpdated                                 71.4%
github.com/skupperproject/skupper/pkg/kube/site/bindings.go:103:                ListenerDeleted                                 100.0%
github.com/skupperproject/skupper/pkg/kube/site/bindings.go:128:                updateBridgeConfigForListener                   66.7%
```

Btw, in order to get coverage to 100%, I believe that I'd need to change BindingAdapter.mapping to be an interface instead of type *qdr.PortMapping.
https://github.com/skupperproject/skupper/blob/v2/pkg/kube/site/bindings.go#L31

This would let the unit test spoof the return value of PortMapping.GetPortForKey().

Just thought I'd mention.  I did not add an interface in this PR to increase test coverage.  

Patch:
diff --git a/cmd/network-console-collector/README.md b/cmd/network-console-collector/README.md
index 2c73a553..97b199f7 100644
--- a/cmd/network-console-collector/README.md
+++ b/cmd/network-console-collector/README.md
@@ -43,8 +43,8 @@ API at `/metrics`.
 
 ### Operational Site Metrics
 
-This set of metrics exposes coarse details pertaining to the operation of a
-site.
+This set of metrics exposes coarse site-level details pertaining to the
+operation and topology of the skupper network.
 
 * `skupper_site_info`: Metadata about the active sites in the network. Labels are `site_id`, `name` and `version`.
 * `skupper_routers_total`: Number of active routers in each site. Labels are `site_id` and `mode`.
diff --git a/cmd/network-console-collector/internal/collector/collector.go b/cmd/network-console-collector/internal/collector/collector.go
index 29d347ea..ca29979f 100644
--- a/cmd/network-console-collector/internal/collector/collector.go
+++ b/cmd/network-console-collector/internal/collector/collector.go
@@ -15,6 +15,7 @@ import (
 	"time"
 
 	"github.com/prometheus/client_golang/prometheus"
+	opmetrics "github.com/skupperproject/skupper/cmd/network-console-collector/internal/collector/metrics"
 	"github.com/skupperproject/skupper/pkg/vanflow"
 	"github.com/skupperproject/skupper/pkg/vanflow/eventsource"
 	"github.com/skupperproject/skupper/pkg/vanflow/session"
@@ -26,15 +27,16 @@ func New(logger *slog.Logger, factory session.ContainerFactory, reg *prometheus.
 	sessionCtr := factory.Create()
 
 	collector := &Collector{
-		logger:        logger,
-		flowRecordTTL: flowRecordTTL,
-		session:       sessionCtr,
-		discovery:     eventsource.NewDiscovery(sessionCtr, eventsource.DiscoveryOptions{}),
-		sources:       make(map[string]eventSource),
-		events:        make(chan changeEvent, 1024),
-		purgeQueue:    make(chan store.SourceRef, 8),
-		recordRouting: make(eventsource.RecordStoreMap),
-		metrics:       register(reg),
+		logger:         logger,
+		flowRecordTTL:  flowRecordTTL,
+		session:        sessionCtr,
+		discovery:      eventsource.NewDiscovery(sessionCtr, eventsource.DiscoveryOptions{}),
+		sources:        make(map[string]eventSource),
+		events:         make(chan changeEvent, 1024),
+		purgeQueue:     make(chan store.SourceRef, 8),
+		recordRouting:  make(eventsource.RecordStoreMap),
+		metrics:        register(reg),
+		metricsAdaptor: opmetrics.New(reg),
 	}
 
 	collector.Records = store.NewSyncMapStore(store.SyncMapStoreConfig{
@@ -73,6 +75,7 @@ type Collector struct {
 	processManager *processManager
 	addressManager *addressManager
 	pairManager    *pairManager
+	metricsAdaptor *opmetrics.Adaptor
 
 	events     chan changeEvent
 	purgeQueue chan store.SourceRef
@@ -139,12 +142,23 @@ func (c *Collector) monitoring(ctx context.Context) func() error {
 	}
 }
 
+func (c *Collector) dispatchMetricsEvents(event changeEvent, _ readonly) {
+	switch event := event.(type) {
+	case addEvent:
+		c.metricsAdaptor.Add(event.Record)
+	case updateEvent:
+		c.metricsAdaptor.Update(event.Prev, event.Curr)
+	case deleteEvent:
+		c.metricsAdaptor.Remove(event.Record)
+	}
+}
+
 func (c *Collector) runWorkQueue(ctx context.Context) func() error {
 	// reactors respond to record changes. Should be quick, and perform minimal
 	// read-only store ops. Handle any changes out of band.
 	reactors := map[vanflow.TypeMeta][]func(event changeEvent, stor readonly){}
 	for _, r := range standardRecordTypes {
-		reactors[r] = append(reactors[r], c.updateGraph)
+		reactors[r] = append(reactors[r], c.updateGraph, c.dispatchMetricsEvents)
 	}
 
 	reactors[AddressRecord{}.GetTypeMeta()] = append(reactors[AddressRecord{}.GetTypeMeta()], c.updateGraph)
diff --git a/cmd/network-console-collector/internal/collector/metrics.go b/cmd/network-console-collector/internal/collector/metrics.go
index e84973ad..045f1d95 100644
--- a/cmd/network-console-collector/internal/collector/metrics.go
+++ b/cmd/network-console-collector/internal/collector/metrics.go
@@ -1,6 +1,8 @@
 package collector
 
-import "github.com/prometheus/client_golang/prometheus"
+import (
+	"github.com/prometheus/client_golang/prometheus"
+)
 
 type metrics struct {
 	flowOpenedCounter        *prometheus.CounterVec
@@ -48,6 +50,7 @@ func register(reg *prometheus.Registry) metrics {
 			Name:      "requests_total",
 			Help:      "Counter incremented for each request handled through the skupper network",
 		}, appFlowMetricLables),
+
 		internal: metricsInternal{
 			flowLatency: prometheus.NewHistogramVec(prometheus.HistogramOpts{
 				Namespace: "skupper",
diff --git a/cmd/network-console-collector/internal/collector/metrics/metrics.go b/cmd/network-console-collector/internal/collector/metrics/metrics.go
new file mode 100644
index 00000000..2df5f0ae
--- /dev/null
+++ b/cmd/network-console-collector/internal/collector/metrics/metrics.go
@@ -0,0 +1,533 @@
+package metrics
+
+import (
+	"github.com/prometheus/client_golang/prometheus"
+	"github.com/skupperproject/skupper/pkg/vanflow"
+)
+
+var (
+	siteInfoMetricLabels = []string{
+		"site_id",
+		"name",
+		"version",
+	}
+	routerInfoMetricLabels = []string{
+		"site_id",
+		"mode",
+	}
+	linkInfoMetricLabels = []string{
+		"site_id",
+		"role",
+		"status",
+	}
+	linkErrorMetricLablels = []string{
+		"site_id",
+		"role",
+	}
+	siteIDMetricLabels = []string{"site_id"}
+)
+
+func New(reg prometheus.Registerer) *Adaptor {
+	h := &Adaptor{
+		sites:         make(map[siteInfo]prometheus.Gauge),
+		routers:       make(map[siteRouters]*gaugeMetricByID),
+		links:         make(map[siteLinks]*gaugeMetricByID),
+		listeners:     make(map[string]*gaugeMetricByID),
+		connectors:    make(map[string]*gaugeMetricByID),
+		linkErrors:    make(map[siteLinkErrors]*counterMetricByItem),
+		pendingRouter: make(map[string]map[string]vanflow.Record),
+	}
+	h.siteInfo = prometheus.NewGaugeVec(prometheus.GaugeOpts{
+		Namespace: "skupper",
+		Name:      "site_info",
+		Help:      "Metadata about the active sites that make up the application network",
+	}, siteInfoMetricLabels)
+	h.routerInfo = prometheus.NewGaugeVec(prometheus.GaugeOpts{
+		Namespace: "skupper",
+		Name:      "routers_total",
+		Help:      "Number of active routers participating in the application network",
+	}, routerInfoMetricLabels)
+	h.siteListenerInfo = prometheus.NewGaugeVec(prometheus.GaugeOpts{
+		Namespace: "skupper",
+		Name:      "site_listeners_total",
+		Help:      "Number of listeners configured in the application network",
+	}, siteIDMetricLabels)
+	h.siteConnectorInfo = prometheus.NewGaugeVec(prometheus.GaugeOpts{
+		Namespace: "skupper",
+		Name:      "site_connectors_total",
+		Help:      "Number of connectors configured in the application network",
+	}, siteIDMetricLabels)
+	h.siteLinkInfo = prometheus.NewGaugeVec(prometheus.GaugeOpts{
+		Namespace: "skupper",
+		Name:      "site_links_total",
+		Help:      "Number of router links configured in the application network",
+	}, linkInfoMetricLabels)
+	h.siteLinkErrors = prometheus.NewCounterVec(prometheus.CounterOpts{
+		Namespace: "skupper",
+		Name:      "site_link_errors_total",
+		Help:      "Count of link connection errors across the application network",
+	}, linkErrorMetricLablels)
+
+	reg.MustRegister(
+		h.siteInfo,
+		h.routerInfo,
+		h.siteLinkInfo,
+		h.siteListenerInfo,
+		h.siteConnectorInfo,
+		h.siteLinkErrors,
+	)
+	return h
+}
+
+// Adaptor between vanflow events and prometheus metrics. Maintains a set of
+// coarse metrics pertaining to network topology.
+type Adaptor struct {
+	siteInfo          *prometheus.GaugeVec
+	routerInfo        *prometheus.GaugeVec
+	siteLinkInfo      *prometheus.GaugeVec
+	siteListenerInfo  *prometheus.GaugeVec
+	siteConnectorInfo *prometheus.GaugeVec
+	siteLinkErrors    *prometheus.CounterVec
+
+	sites      map[siteInfo]prometheus.Gauge
+	routers    map[siteRouters]*gaugeMetricByID
+	links      map[siteLinks]*gaugeMetricByID
+	listeners  map[string]*gaugeMetricByID
+	connectors map[string]*gaugeMetricByID
+	linkErrors map[siteLinkErrors]*counterMetricByItem
+
+	pendingRouter map[string]map[string]vanflow.Record
+
+	routerSitesCache map[string]string
+}
+
+type counterMetricByItem struct {
+	Items   map[string]int
+	Counter prometheus.Counter
+}
+
+func (c *counterMetricByItem) Ensure(id string, ct int) {
+	prev, ok := c.Items[id]
+	if !ok {
+		prev = ct
+		c.Items[id] = prev
+	}
+	if ct <= prev {
+		return
+	}
+	c.Items[id] = ct
+	c.Counter.Add(float64(ct - prev))
+}
+
+func (m *counterMetricByItem) Remove(id string) {
+	if _, ok := m.Items[id]; !ok {
+		return
+	}
+	delete(m.Items, id)
+}
+
+type gaugeMetricByID struct {
+	IDs   map[string]struct{}
+	Gauge prometheus.Gauge
+}
+
+func (m *gaugeMetricByID) Ensure(id string) bool {
+	if _, ok := m.IDs[id]; ok {
+		return false
+	}
+	m.IDs[id] = struct{}{}
+	m.Gauge.Add(1.0)
+	return true
+}
+
+func (m *gaugeMetricByID) Remove(id string) {
+	if _, ok := m.IDs[id]; !ok {
+		return
+	}
+	delete(m.IDs, id)
+	m.Gauge.Sub(1.0)
+}
+
+func toSiteInfo(site vanflow.SiteRecord) (siteInfo, bool) {
+	var info siteInfo
+	if site.Name == nil || site.Version == nil {
+		return info, false
+	}
+	info.ID, info.Name, info.Version = site.ID, *site.Name, *site.Version
+	return info, true
+}
+
+func routerInfo(router vanflow.RouterRecord) (siteRouters, bool) {
+	if router.Parent == nil || router.Mode == nil {
+		return siteRouters{}, false
+	}
+	return siteRouters{
+		SiteID: *router.Parent,
+		Mode:   *router.Mode,
+	}, true
+}
+
+func (a *Adaptor) linkInfo(link vanflow.LinkRecord) (info siteLinks, wants string, ok bool) {
+	if link.Parent == nil || link.Role == nil || link.Status == nil {
+		return siteLinks{}, "", false
+	}
+	siteID, ok := a.siteIDForRotuer(*link.Parent)
+	if !ok {
+		return siteLinks{}, *link.Parent, false
+	}
+	return siteLinks{
+		SiteID: siteID,
+		Role:   *link.Role,
+		Status: *link.Status,
+	}, "", true
+}
+
+func (a *Adaptor) listenerInfo(listener vanflow.ListenerRecord) (siteID, wants string, ok bool) {
+	if listener.Parent == nil {
+		return "", "", false
+	}
+	siteID, ok = a.siteIDForRotuer(*listener.Parent)
+	if !ok {
+		return "", *listener.Parent, false
+	}
+	return siteID, "", true
+}
+
+func (a *Adaptor) connectorInfo(connector vanflow.ConnectorRecord) (siteID, wants string, ok bool) {
+	if connector.Parent == nil {
+		return "", "", false
+	}
+	siteID, ok = a.siteIDForRotuer(*connector.Parent)
+	if !ok {
+		return "", *connector.Parent, false
+	}
+	return siteID, "", true
+}
+
+func (a *Adaptor) siteIDForRotuer(rID string) (siteID string, ok bool) {
+	if a.routerSitesCache == nil {
+		a.routerSitesCache = make(map[string]string)
+	}
+	if siteID, ok := a.routerSitesCache[rID]; ok {
+		return siteID, true
+	}
+	for site, routers := range a.routers {
+		_, found := routers.IDs[rID]
+		if found {
+			ok = true
+			siteID = site.SiteID
+			break
+		}
+	}
+	if ok {
+		a.routerSitesCache[rID] = siteID
+	}
+	return siteID, ok
+}
+
+func (a *Adaptor) Add(record vanflow.Record) {
+	switch record := record.(type) {
+	case vanflow.SiteRecord:
+		info, ok := toSiteInfo(record)
+		if !ok {
+			return
+		}
+		if _, ok := a.sites[info]; ok {
+			return
+		}
+		a.sites[info] = a.siteInfo.With(info.asLabels())
+		a.sites[info].Set(1.0)
+	case vanflow.RouterRecord:
+		siteRouter, ok := routerInfo(record)
+		if !ok {
+			return
+		}
+		metric, ok := a.routers[siteRouter]
+		if !ok {
+			metric = a.newSiteRouterMetrics(siteRouter)
+			a.routers[siteRouter] = metric
+		}
+		if added := metric.Ensure(record.ID); added {
+			dependencies, ok := a.pendingRouter[record.ID]
+			if ok {
+				delete(a.pendingRouter, record.ID)
+			}
+			for _, record := range dependencies {
+				a.Add(record)
+			}
+		}
+	case vanflow.LinkRecord:
+		siteLink, wants, ok := a.linkInfo(record)
+		if !ok {
+			if wants != "" {
+				a.addPendingRouter(wants, record)
+			}
+			return
+		}
+		metric, ok := a.links[siteLink]
+		if !ok {
+			metric = a.newSiteLinkMetrics(siteLink)
+			a.links[siteLink] = metric
+		}
+		metric.Ensure(record.ID)
+
+		if errCount := record.DownCount; errCount != nil {
+			linkErrorKey := siteLinkErrors{
+				SiteID: siteLink.SiteID,
+				Role:   siteLink.Role,
+			}
+			counter, ok := a.linkErrors[linkErrorKey]
+			if !ok {
+				counter = a.newLinkErrorMetrics(linkErrorKey)
+				a.linkErrors[linkErrorKey] = counter
+			}
+			counter.Ensure(record.ID, int(*errCount))
+		}
+	case vanflow.ListenerRecord:
+		site, wants, ok := a.listenerInfo(record)
+		if !ok {
+			if wants != "" {
+				a.addPendingRouter(wants, record)
+			}
+			return
+		}
+		metric, ok := a.listeners[site]
+		if !ok {
+			metric = a.newSiteListenerMetrics(site)
+			a.listeners[site] = metric
+		}
+		metric.Ensure(record.ID)
+	case vanflow.ConnectorRecord:
+		site, wants, ok := a.connectorInfo(record)
+		if !ok {
+			if wants != "" {
+				a.addPendingRouter(wants, record)
+			}
+			return
+		}
+		metric, ok := a.connectors[site]
+		if !ok {
+			metric = a.newSiteConnectorMetrics(site)
+			a.connectors[site] = metric
+		}
+		metric.Ensure(record.ID)
+	}
+}
+
+func (a *Adaptor) addPendingRouter(rID string, record vanflow.Record) {
+	pending, ok := a.pendingRouter[rID]
+	if !ok {
+		pending = make(map[string]vanflow.Record)
+		a.pendingRouter[rID] = pending
+	}
+	pending[record.Identity()] = record
+}
+
+func (a *Adaptor) clearPendingRouter(rID string, record vanflow.Record) {
+	pending, ok := a.pendingRouter[rID]
+	if !ok {
+		return
+	}
+	delete(pending, record.Identity())
+}
+
+func (a *Adaptor) Update(prev, curr vanflow.Record) {
+	switch record := curr.(type) {
+	case vanflow.SiteRecord:
+		a.Add(curr)
+	case vanflow.RouterRecord:
+		a.Add(curr)
+	case vanflow.ListenerRecord:
+		a.Add(curr)
+	case vanflow.ConnectorRecord:
+		a.Add(curr)
+	case vanflow.LinkRecord:
+		siteLink, wants, ok := a.linkInfo(record)
+		if !ok {
+			if wants != "" {
+				a.addPendingRouter(wants, record)
+			}
+			return
+		}
+		prevLink, _, ok := a.linkInfo(prev.(vanflow.LinkRecord))
+		if ok && prevLink != siteLink {
+			a.Remove(prev)
+		}
+		metric, ok := a.links[siteLink]
+		if !ok {
+			metric = a.newSiteLinkMetrics(siteLink)
+			a.links[siteLink] = metric
+		}
+		metric.Ensure(record.ID)
+		if errCount := record.DownCount; errCount != nil {
+			linkErrorKey := siteLinkErrors{
+				SiteID: siteLink.SiteID,
+				Role:   siteLink.Role,
+			}
+			counter, ok := a.linkErrors[linkErrorKey]
+			if !ok {
+				counter = a.newLinkErrorMetrics(linkErrorKey)
+				a.linkErrors[linkErrorKey] = counter
+			}
+			counter.Ensure(record.ID, int(*errCount))
+		}
+	}
+}
+
+func (a *Adaptor) Remove(record vanflow.Record) {
+	switch record := record.(type) {
+	case vanflow.SiteRecord:
+		info, ok := toSiteInfo(record)
+		if !ok {
+			return
+		}
+		m, ok := a.sites[info]
+		if !ok {
+			return
+		}
+		m.Set(0.0)
+		a.sites[info] = nil
+		delete(a.sites, info)
+	case vanflow.RouterRecord:
+		siteRouter, ok := routerInfo(record)
+		if !ok {
+			return
+		}
+		metric, ok := a.routers[siteRouter]
+		if !ok {
+			return
+		}
+		metric.Remove(record.ID)
+	case vanflow.LinkRecord:
+		siteLink, wants, ok := a.linkInfo(record)
+		if !ok {
+			if wants != "" {
+				a.clearPendingRouter(wants, record)
+			}
+			return
+		}
+		metric, ok := a.links[siteLink]
+		if !ok {
+			return
+		}
+		metric.Remove(record.ID)
+		linkErrorKey := siteLinkErrors{
+			SiteID: siteLink.SiteID,
+			Role:   siteLink.Role,
+		}
+		counter, ok := a.linkErrors[linkErrorKey]
+		if !ok {
+			return
+		}
+		counter.Remove(record.ID)
+	case vanflow.ListenerRecord:
+		site, wants, ok := a.listenerInfo(record)
+		if !ok {
+			if wants != "" {
+				a.clearPendingRouter(wants, record)
+			}
+			return
+		}
+		metric, ok := a.listeners[site]
+		if !ok {
+			return
+		}
+		metric.Remove(record.ID)
+	case vanflow.ConnectorRecord:
+		site, wants, ok := a.connectorInfo(record)
+		if !ok {
+			if wants != "" {
+				a.clearPendingRouter(wants, record)
+			}
+			return
+		}
+		metric, ok := a.connectors[site]
+		if !ok {
+			return
+		}
+		metric.Remove(record.ID)
+	}
+}
+
+func (a *Adaptor) newSiteConnectorMetrics(siteID string) *gaugeMetricByID {
+	return &gaugeMetricByID{
+		IDs:   make(map[string]struct{}),
+		Gauge: a.siteConnectorInfo.WithLabelValues(siteID),
+	}
+}
+func (a *Adaptor) newSiteListenerMetrics(siteID string) *gaugeMetricByID {
+	return &gaugeMetricByID{
+		IDs:   make(map[string]struct{}),
+		Gauge: a.siteListenerInfo.WithLabelValues(siteID),
+	}
+}
+func (a *Adaptor) newSiteLinkMetrics(labels siteLinks) *gaugeMetricByID {
+	return &gaugeMetricByID{
+		IDs:   make(map[string]struct{}),
+		Gauge: a.siteLinkInfo.With(labels.asLabels()),
+	}
+}
+func (a *Adaptor) newSiteRouterMetrics(labels siteRouters) *gaugeMetricByID {
+	return &gaugeMetricByID{
+		IDs:   make(map[string]struct{}),
+		Gauge: a.routerInfo.With(labels.asLabels()),
+	}
+}
+func (a *Adaptor) newLinkErrorMetrics(labels siteLinkErrors) *counterMetricByItem {
+	return &counterMetricByItem{
+		Items:   make(map[string]int),
+		Counter: a.siteLinkErrors.With(labels.asLabels()),
+	}
+}
+
+type siteInfo struct {
+	ID      string
+	Name    string
+	Version string
+}
+
+func (i siteInfo) asLabels() prometheus.Labels {
+	return prometheus.Labels{
+		"site_id": i.ID,
+		"name":    i.Name,
+		"version": i.Version,
+	}
+}
+
+type siteRouters struct {
+	SiteID string
+	Mode   string
+}
+
+func (i siteRouters) asLabels() prometheus.Labels {
+	return prometheus.Labels{
+		"site_id": i.SiteID,
+		"mode":    i.Mode,
+	}
+}
+
+type siteLinks struct {
+	SiteID string
+	Role   string
+	Status string
+}
+
+func (i siteLinks) asLabels() prometheus.Labels {
+	return prometheus.Labels{
+		"site_id": i.SiteID,
+		"role":    i.Role,
+		"status":  i.Status,
+	}
+}
+
+type siteLinkErrors struct {
+	SiteID string
+	Role   string
+}
+
+func (i siteLinkErrors) asLabels() prometheus.Labels {
+	return prometheus.Labels{
+		"site_id": i.SiteID,
+		"role":    i.Role,
+	}
+}
diff --git a/internal/cmd/skupper/common/flags.go b/internal/cmd/skupper/common/flags.go
index 97e8cfe8..418b948e 100644
--- a/internal/cmd/skupper/common/flags.go
+++ b/internal/cmd/skupper/common/flags.go
@@ -29,8 +29,8 @@ for other Kubernetes flavors, loadbalancer is the default.`
 	FlagNameSubjectAlternativeNames = "subject-alternative-names"
 	FlagDescSubjectAlternativeNames = "Add subject alternative names for the router access in non kubernetes environments"
 
-	FlagNameTlsSecret          = "tls-secret"
-	FlagDescTlsSecret          = "the name of a Kubernetes secret containing the generated or externally-supplied TLS credentials."
+	FlagNameTlsCredentials     = "tls-credentials"
+	FlagDescTlsCredentials     = "the name of a Kubernetes secret containing the generated or externally-supplied TLS credentials."
 	FlagNameCost               = "cost"
 	FlagDescCost               = "the configured \"expense\" of sending traffic over the link."
 	FlagNameGenerateCredential = "generate-credential"
@@ -99,17 +99,17 @@ type CommandSiteDeleteFlags struct {
 }
 
 type CommandLinkGenerateFlags struct {
-	TlsSecret          string
+	TlsCredentials     string
 	Cost               string
 	Output             string
 	GenerateCredential bool
 	Timeout            time.Duration
 }
 type CommandLinkUpdateFlags struct {
-	TlsSecret string
-	Cost      string
-	Output    string
-	Timeout   time.Duration
+	TlsCredentials string
+	Cost           string
+	Output         string
+	Timeout        time.Duration
 }
 
 type CommandLinkDeleteFlags struct {
@@ -134,7 +134,7 @@ type CommandConnectorCreateFlags struct {
 	RoutingKey      string
 	Host            string
 	Selector        string
-	TlsSecret       string
+	TlsCredentials  string
 	ConnectorType   string
 	IncludeNotReady bool
 	Workload        string
@@ -145,7 +145,7 @@ type CommandConnectorCreateFlags struct {
 type CommandConnectorUpdateFlags struct {
 	RoutingKey      string
 	Host            string
-	TlsSecret       string
+	TlsCredentials  string
 	ConnectorType   string
 	Port            int
 	Workload        string
@@ -164,22 +164,22 @@ type CommandConnectorStatusFlags struct {
 }
 
 type CommandListenerCreateFlags struct {
-	RoutingKey   string
-	Host         string
-	TlsSecret    string
-	ListenerType string
-	Timeout      time.Duration
-	Output       string
+	RoutingKey     string
+	Host           string
+	TlsCredentials string
+	ListenerType   string
+	Timeout        time.Duration
+	Output         string
 }
 
 type CommandListenerUpdateFlags struct {
-	RoutingKey   string
-	Host         string
-	TlsSecret    string
-	ListenerType string
-	Timeout      time.Duration
-	Port         int
-	Output       string
+	RoutingKey     string
+	Host           string
+	TlsCredentials string
+	ListenerType   string
+	Timeout        time.Duration
+	Port           int
+	Output         string
 }
 
 type CommandListenerStatusFlags struct {
diff --git a/internal/cmd/skupper/connector/connector.go b/internal/cmd/skupper/connector/connector.go
index 7f8b2b24..900d276c 100644
--- a/internal/cmd/skupper/connector/connector.go
+++ b/internal/cmd/skupper/connector/connector.go
@@ -46,7 +46,7 @@ skupper connector create backend 8080 --workload deployment/backend`,
 
 	cmd.Flags().StringVarP(&cmdFlags.RoutingKey, common.FlagNameRoutingKey, "r", "", common.FlagDescRoutingKey)
 	cmd.Flags().StringVar(&cmdFlags.Host, common.FlagNameHost, "", common.FlagDescHost)
-	cmd.Flags().StringVar(&cmdFlags.TlsSecret, common.FlagNameTlsSecret, "", common.FlagDescTlsSecret)
+	cmd.Flags().StringVar(&cmdFlags.TlsCredentials, common.FlagNameTlsCredentials, "", common.FlagDescTlsCredentials)
 	cmd.Flags().StringVar(&cmdFlags.ConnectorType, common.FlagNameConnectorType, "tcp", common.FlagDescConnectorType)
 	cmd.Flags().BoolVarP(&cmdFlags.IncludeNotReady, common.FlagNameIncludeNotReady, "i", false, common.FlagDescIncludeNotRead)
 	cmd.Flags().StringVarP(&cmdFlags.Selector, common.FlagNameSelector, "s", "", common.FlagDescSelector)
@@ -110,7 +110,7 @@ func CmdConnectorUpdateFactory(configuredPlatform types.Platform) *cobra.Command
 
 	cmd.Flags().StringVarP(&cmdFlags.RoutingKey, common.FlagNameRoutingKey, "r", "", common.FlagDescRoutingKey)
 	cmd.Flags().StringVar(&cmdFlags.Host, common.FlagNameHost, "", common.FlagDescHost)
-	cmd.Flags().StringVar(&cmdFlags.TlsSecret, common.FlagNameTlsSecret, "", common.FlagDescTlsSecret)
+	cmd.Flags().StringVar(&cmdFlags.TlsCredentials, common.FlagNameTlsCredentials, "", common.FlagDescTlsCredentials)
 	cmd.Flags().StringVar(&cmdFlags.ConnectorType, common.FlagNameConnectorType, "tcp", common.FlagDescConnectorType)
 	cmd.Flags().BoolVarP(&cmdFlags.IncludeNotReady, common.FlagNameIncludeNotReady, "i", false, common.FlagDescIncludeNotRead)
 	cmd.Flags().StringVarP(&cmdFlags.Selector, common.FlagNameSelector, "s", "", common.FlagDescSelector)
diff --git a/internal/cmd/skupper/connector/kube/connector_create.go b/internal/cmd/skupper/connector/kube/connector_create.go
index c4055bc1..64f59517 100644
--- a/internal/cmd/skupper/connector/kube/connector_create.go
+++ b/internal/cmd/skupper/connector/kube/connector_create.go
@@ -30,7 +30,7 @@ type CmdConnectorCreate struct {
 	output          string
 	host            string
 	selector        string
-	tlsSecret       string
+	tlsCredentials  string
 	routingKey      string
 	connectorType   string
 	includeNotReady bool
@@ -103,9 +103,9 @@ func (cmd *CmdConnectorCreate) ValidateInput(args []string) []error {
 			validationErrors = append(validationErrors, fmt.Errorf("routing key is not valid: %s", err))
 		}
 	}
-	if cmd.Flags != nil && cmd.Flags.TlsSecret != "" {
+	if cmd.Flags != nil && cmd.Flags.TlsCredentials != "" {
 		// check that the secret exists
-		_, err := cmd.KubeClient.CoreV1().Secrets(cmd.namespace).Get(context.TODO(), cmd.Flags.TlsSecret, metav1.GetOptions{})
+		_, err := cmd.KubeClient.CoreV1().Secrets(cmd.namespace).Get(context.TODO(), cmd.Flags.TlsCredentials, metav1.GetOptions{})
 		if err != nil {
 			validationErrors = append(validationErrors, fmt.Errorf("tls-secret is not valid: does not exist"))
 		}
@@ -227,7 +227,7 @@ func (cmd *CmdConnectorCreate) InputToOptions() {
 		cmd.routingKey = cmd.Flags.RoutingKey
 	}
 	cmd.timeout = cmd.Flags.Timeout
-	cmd.tlsSecret = cmd.Flags.TlsSecret
+	cmd.tlsCredentials = cmd.Flags.TlsCredentials
 	cmd.connectorType = cmd.Flags.ConnectorType
 	cmd.output = cmd.Flags.Output
 	cmd.includeNotReady = cmd.Flags.IncludeNotReady
@@ -248,7 +248,7 @@ func (cmd *CmdConnectorCreate) Run() error {
 			Host:            cmd.host,
 			Port:            cmd.port,
 			RoutingKey:      cmd.routingKey,
-			TlsCredentials:  cmd.tlsSecret,
+			TlsCredentials:  cmd.tlsCredentials,
 			Type:            cmd.connectorType,
 			IncludeNotReady: cmd.includeNotReady,
 			Selector:        cmd.selector,
diff --git a/internal/cmd/skupper/connector/kube/connector_update.go b/internal/cmd/skupper/connector/kube/connector_update.go
index 62b6147b..eb8b278d 100644
--- a/internal/cmd/skupper/connector/kube/connector_update.go
+++ b/internal/cmd/skupper/connector/kube/connector_update.go
@@ -22,7 +22,7 @@ import (
 type ConnectorUpdates struct {
 	routingKey      string
 	host            string
-	tlsSecret       string
+	tlsCredentials  string
 	connectorType   string
 	port            int
 	workload        string
@@ -95,7 +95,7 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) []error {
 			// save existing values
 			cmd.resourceVersion = connector.ResourceVersion
 			cmd.newSettings.port = connector.Spec.Port
-			cmd.newSettings.tlsSecret = connector.Spec.TlsCredentials
+			cmd.newSettings.tlsCredentials = connector.Spec.TlsCredentials
 			cmd.newSettings.connectorType = connector.Spec.Type
 			cmd.newSettings.includeNotReady = connector.Spec.IncludeNotReady
 			cmd.newSettings.routingKey = connector.Spec.RoutingKey
@@ -113,12 +113,12 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) []error {
 			cmd.newSettings.routingKey = cmd.Flags.RoutingKey
 		}
 	}
-	if cmd.Flags != nil && cmd.Flags.TlsSecret != "" {
-		_, err := cmd.KubeClient.CoreV1().Secrets(cmd.namespace).Get(context.TODO(), cmd.Flags.TlsSecret, metav1.GetOptions{})
+	if cmd.Flags != nil && cmd.Flags.TlsCredentials != "" {
+		_, err := cmd.KubeClient.CoreV1().Secrets(cmd.namespace).Get(context.TODO(), cmd.Flags.TlsCredentials, metav1.GetOptions{})
 		if err != nil {
 			validationErrors = append(validationErrors, fmt.Errorf("tls-secret is not valid: does not exist"))
 		} else {
-			cmd.newSettings.tlsSecret = cmd.Flags.TlsSecret
+			cmd.newSettings.tlsCredentials = cmd.Flags.TlsCredentials
 		}
 	}
 	if cmd.Flags != nil && cmd.Flags.ConnectorType != "" {
@@ -254,7 +254,7 @@ func (cmd *CmdConnectorUpdate) Run() error {
 			Host:            cmd.newSettings.host,
 			Port:            cmd.newSettings.port,
 			RoutingKey:      cmd.newSettings.routingKey,
-			TlsCredentials:  cmd.newSettings.tlsSecret,
+			TlsCredentials:  cmd.newSettings.tlsCredentials,
 			Type:            cmd.newSettings.connectorType,
 			Selector:        cmd.newSettings.selector,
 			IncludeNotReady: cmd.newSettings.includeNotReady,
diff --git a/internal/cmd/skupper/connector/nonkube/connector_create.go b/internal/cmd/skupper/connector/nonkube/connector_create.go
index f3dcd4bc..e4075963 100644
--- a/internal/cmd/skupper/connector/nonkube/connector_create.go
+++ b/internal/cmd/skupper/connector/nonkube/connector_create.go
@@ -25,7 +25,7 @@ type CmdConnectorCreate struct {
 	host             string
 	routingKey       string
 	connectorType    string
-	tlsSecret        string
+	tlsCredentials   string
 }
 
 func NewCmdConnectorCreate() *CmdConnectorCreate {
@@ -111,8 +111,8 @@ func (cmd *CmdConnectorCreate) ValidateInput(args []string) []error {
 	} else {
 		validationErrors = append(validationErrors, fmt.Errorf("host name must be configured: an IP address or hostname is expected"))
 	}
-	if cmd.Flags.TlsSecret != "" {
-		// TBD what is valid TlsSecret
+	if cmd.Flags.TlsCredentials != "" {
+		// TBD what is valid TlsCredentials
 	}
 
 	return validationErrors
@@ -133,7 +133,7 @@ func (cmd *CmdConnectorCreate) InputToOptions() {
 	cmd.host = cmd.Flags.Host
 	cmd.connectorType = cmd.Flags.ConnectorType
 	cmd.output = cmd.Flags.Output
-	cmd.tlsSecret = cmd.Flags.TlsSecret
+	cmd.tlsCredentials = cmd.Flags.TlsCredentials
 }
 
 func (cmd *CmdConnectorCreate) Run() error {
@@ -150,7 +150,7 @@ func (cmd *CmdConnectorCreate) Run() error {
 			Host:           cmd.host,
 			Port:           cmd.port,
 			RoutingKey:     cmd.routingKey,
-			TlsCredentials: cmd.tlsSecret,
+			TlsCredentials: cmd.tlsCredentials,
 			Type:           cmd.connectorType,
 		},
 	}
diff --git a/internal/cmd/skupper/connector/nonkube/connector_update.go b/internal/cmd/skupper/connector/nonkube/connector_update.go
index 5358950d..c495dcdd 100644
--- a/internal/cmd/skupper/connector/nonkube/connector_update.go
+++ b/internal/cmd/skupper/connector/nonkube/connector_update.go
@@ -14,11 +14,11 @@ import (
 )
 
 type ConnectorUpdates struct {
-	routingKey    string
-	host          string
-	connectorType string
-	port          int
-	tlsSecret     string
+	routingKey     string
+	host           string
+	connectorType  string
+	port           int
+	tlsCredentials string
 }
 type CmdConnectorUpdate struct {
 	connectorHandler *fs.ConnectorHandler
@@ -85,7 +85,7 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) []error {
 			cmd.newSettings.host = connector.Spec.Host
 			cmd.newSettings.port = connector.Spec.Port
 			cmd.newSettings.connectorType = connector.Spec.Type
-			cmd.newSettings.tlsSecret = connector.Spec.TlsCredentials
+			cmd.newSettings.tlsCredentials = connector.Spec.TlsCredentials
 			cmd.newSettings.routingKey = connector.Spec.RoutingKey
 		}
 	}
@@ -156,7 +156,7 @@ func (cmd *CmdConnectorUpdate) Run() error {
 			Host:           cmd.newSettings.host,
 			Port:           cmd.newSettings.port,
 			RoutingKey:     cmd.newSettings.routingKey,
-			TlsCredentials: cmd.newSettings.tlsSecret,
+			TlsCredentials: cmd.newSettings.tlsCredentials,
 			Type:           cmd.newSettings.connectorType,
 		},
 	}
diff --git a/internal/cmd/skupper/link/kube/link_generate.go b/internal/cmd/skupper/link/kube/link_generate.go
index e8575e35..86a64cf5 100644
--- a/internal/cmd/skupper/link/kube/link_generate.go
+++ b/internal/cmd/skupper/link/kube/link_generate.go
@@ -29,7 +29,7 @@ type CmdLinkGenerate struct {
 	Flags              *common.CommandLinkGenerateFlags
 	linkName           string
 	Namespace          string
-	tlsSecret          string
+	tlsCredentials     string
 	cost               int
 	output             string
 	activeSite         *v2alpha1.Site
@@ -82,10 +82,10 @@ func (cmd *CmdLinkGenerate) ValidateInput(args []string) []error {
 		validationErrors = append(validationErrors, fmt.Errorf("arguments are not allowed in this command"))
 	}
 
-	if cmd.Flags.TlsSecret == "" && !cmd.Flags.GenerateCredential {
+	if cmd.Flags.TlsCredentials == "" && !cmd.Flags.GenerateCredential {
 		validationErrors = append(validationErrors, fmt.Errorf("the TLS secret name was not specified"))
-	} else if cmd.Flags.TlsSecret != "" {
-		ok, err := resourceStringValidator.Evaluate(cmd.Flags.TlsSecret)
+	} else if cmd.Flags.TlsCredentials != "" {
+		ok, err := resourceStringValidator.Evaluate(cmd.Flags.TlsCredentials)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("the name of the tls secret is not valid: %s", err))
 		}
@@ -129,10 +129,10 @@ func (cmd *CmdLinkGenerate) InputToOptions() {
 
 	cmd.linkName = generatedLinkName
 
-	if cmd.Flags.TlsSecret == "" {
-		cmd.tlsSecret = generatedLinkName
+	if cmd.Flags.TlsCredentials == "" {
+		cmd.tlsCredentials = generatedLinkName
 	} else {
-		cmd.tlsSecret = cmd.Flags.TlsSecret
+		cmd.tlsCredentials = cmd.Flags.TlsCredentials
 	}
 
 }
@@ -158,7 +158,7 @@ func (cmd *CmdLinkGenerate) Run() error {
 			Name: cmd.linkName,
 		},
 		Spec: v2alpha1.LinkSpec{
-			TlsCredentials: cmd.tlsSecret,
+			TlsCredentials: cmd.tlsCredentials,
 			Cost:           cmd.cost,
 			Endpoints:      cmd.activeSite.Status.Endpoints,
 		},
@@ -173,7 +173,7 @@ func (cmd *CmdLinkGenerate) Run() error {
 				Kind:       "Certificate",
 			},
 			ObjectMeta: metav1.ObjectMeta{
-				Name: cmd.tlsSecret,
+				Name: cmd.tlsCredentials,
 			},
 			Spec: v2alpha1.CertificateSpec{
 				Ca:      cmd.activeSite.Status.DefaultIssuer,
@@ -207,7 +207,7 @@ func (cmd *CmdLinkGenerate) WaitUntil() error {
 
 		err := pkgutils.RetryErrorWithContext(ctxWithTimeout, time.Second, func() error {
 
-			generatedSecret, err := cmd.KubeClient.CoreV1().Secrets(cmd.Namespace).Get(context.TODO(), cmd.tlsSecret, metav1.GetOptions{})
+			generatedSecret, err := cmd.KubeClient.CoreV1().Secrets(cmd.Namespace).Get(context.TODO(), cmd.tlsCredentials, metav1.GetOptions{})
 			if err != nil {
 				return err
 			}
@@ -220,7 +220,7 @@ func (cmd *CmdLinkGenerate) WaitUntil() error {
 						Kind:       "Secret",
 					},
 					ObjectMeta: metav1.ObjectMeta{
-						Name: cmd.tlsSecret,
+						Name: cmd.tlsCredentials,
 					},
 					Type: "kubernetes.io/tls",
 					Data: map[string][]byte{
@@ -237,7 +237,7 @@ func (cmd *CmdLinkGenerate) WaitUntil() error {
 		})
 
 		if err != nil {
-			return fmt.Errorf("TLS secret %q not ready yet, check the status for more information\n", cmd.tlsSecret)
+			return fmt.Errorf("TLS secret %q not ready yet, check the status for more information\n", cmd.tlsCredentials)
 		}
 
 	}
@@ -246,7 +246,7 @@ func (cmd *CmdLinkGenerate) WaitUntil() error {
 	printResources(resourcesToPrint, cmd.output)
 
 	if cmd.generateCredential {
-		_, err = cmd.Client.Certificates(cmd.Namespace).Get(context.TODO(), cmd.tlsSecret, metav1.GetOptions{})
+		_, err = cmd.Client.Certificates(cmd.Namespace).Get(context.TODO(), cmd.tlsCredentials, metav1.GetOptions{})
 		if err != nil {
 			return fmt.Errorf("there was an error trying to delete the generated certificate: %s", err)
 		}
diff --git a/internal/cmd/skupper/link/kube/link_update.go b/internal/cmd/skupper/link/kube/link_update.go
index 64d6e3e5..7378fcc9 100644
--- a/internal/cmd/skupper/link/kube/link_update.go
+++ b/internal/cmd/skupper/link/kube/link_update.go
@@ -20,16 +20,16 @@ import (
 )
 
 type CmdLinkUpdate struct {
-	Client     skupperv2alpha1.SkupperV2alpha1Interface
-	KubeClient kubernetes.Interface
-	CobraCmd   *cobra.Command
-	Flags      *common.CommandLinkUpdateFlags
-	linkName   string
-	Namespace  string
-	tlsSecret  string
-	cost       int
-	output     string
-	timeout    time.Duration
+	Client         skupperv2alpha1.SkupperV2alpha1Interface
+	KubeClient     kubernetes.Interface
+	CobraCmd       *cobra.Command
+	Flags          *common.CommandLinkUpdateFlags
+	linkName       string
+	Namespace      string
+	tlsCredentials string
+	cost           int
+	output         string
+	timeout        time.Duration
 }
 
 func NewCmdLinkUpdate() *CmdLinkUpdate {
@@ -70,10 +70,10 @@ func (cmd *CmdLinkUpdate) ValidateInput(args []string) []error {
 		cmd.linkName = args[0]
 	}
 
-	if cmd.Flags.TlsSecret != "" {
-		secret, err := cmd.KubeClient.CoreV1().Secrets(cmd.Namespace).Get(context.TODO(), cmd.Flags.TlsSecret, metav1.GetOptions{})
+	if cmd.Flags.TlsCredentials != "" {
+		secret, err := cmd.KubeClient.CoreV1().Secrets(cmd.Namespace).Get(context.TODO(), cmd.Flags.TlsCredentials, metav1.GetOptions{})
 		if secret == nil || err != nil {
-			validationErrors = append(validationErrors, fmt.Errorf("the TLS secret %q is not available in the namespace: %s", cmd.Flags.TlsSecret, err))
+			validationErrors = append(validationErrors, fmt.Errorf("the TLS secret %q is not available in the namespace: %s", cmd.Flags.TlsCredentials, err))
 		}
 	}
 
@@ -104,7 +104,7 @@ func (cmd *CmdLinkUpdate) ValidateInput(args []string) []error {
 func (cmd *CmdLinkUpdate) InputToOptions() {
 
 	cmd.cost, _ = strconv.Atoi(cmd.Flags.Cost)
-	cmd.tlsSecret = cmd.Flags.TlsSecret
+	cmd.tlsCredentials = cmd.Flags.TlsCredentials
 	cmd.output = cmd.Flags.Output
 	cmd.timeout = cmd.Flags.Timeout
 
@@ -123,9 +123,9 @@ func (cmd *CmdLinkUpdate) Run() error {
 		updatedCost = cmd.cost
 	}
 
-	updatedTlsSecret := currentSite.Spec.TlsCredentials
-	if cmd.tlsSecret != "" {
-		updatedTlsSecret = cmd.tlsSecret
+	updatedTlsCredentials := currentSite.Spec.TlsCredentials
+	if cmd.tlsCredentials != "" {
+		updatedTlsCredentials = cmd.tlsCredentials
 	}
 
 	resource := v2alpha1.Link{
@@ -140,7 +140,7 @@ func (cmd *CmdLinkUpdate) Run() error {
 			ResourceVersion:   currentSite.ResourceVersion,
 		},
 		Spec: v2alpha1.LinkSpec{
-			TlsCredentials: updatedTlsSecret,
+			TlsCredentials: updatedTlsCredentials,
 			Cost:           updatedCost,
 		},
 	}
diff --git a/internal/cmd/skupper/link/link.go b/internal/cmd/skupper/link/link.go
index e6e6246a..38176bb7 100644
--- a/internal/cmd/skupper/link/link.go
+++ b/internal/cmd/skupper/link/link.go
@@ -41,7 +41,7 @@ output needs to be applied in the site in which we want to create the link.`,
 
 	cmd := common.ConfigureCobraCommand(configuredPlatform, cmdLinkGenerateDesc, kubeCommand, nonKubeCommand)
 	cmdFlags := common.CommandLinkGenerateFlags{}
-	cmd.Flags().StringVar(&cmdFlags.TlsSecret, common.FlagNameTlsSecret, "", common.FlagDescTlsSecret)
+	cmd.Flags().StringVar(&cmdFlags.TlsCredentials, common.FlagNameTlsCredentials, "", common.FlagDescTlsCredentials)
 	cmd.Flags().StringVar(&cmdFlags.Cost, common.FlagNameCost, "1", common.FlagDescCost)
 	cmd.Flags().StringVarP(&cmdFlags.Output, common.FlagNameOutput, "o", "yaml", common.FlagDescOutput)
 	cmd.Flags().BoolVar(&cmdFlags.GenerateCredential, common.FlagNameGenerateCredential, true, common.FlagDescGenerateCredential)
@@ -67,7 +67,7 @@ func CmdLinkUpdateFactory(configuredPlatform types.Platform) *cobra.Command {
 
 	cmd := common.ConfigureCobraCommand(configuredPlatform, cmdLinkUpdateDesc, kubeCommand, nonKubeCommand)
 	cmdFlags := common.CommandLinkUpdateFlags{}
-	cmd.Flags().StringVar(&cmdFlags.TlsSecret, common.FlagNameTlsSecret, "", common.FlagDescTlsSecret)
+	cmd.Flags().StringVar(&cmdFlags.TlsCredentials, common.FlagNameTlsCredentials, "", common.FlagDescTlsCredentials)
 	cmd.Flags().StringVar(&cmdFlags.Cost, common.FlagNameCost, "1", common.FlagDescCost)
 	cmd.Flags().DurationVar(&cmdFlags.Timeout, common.FlagNameTimeout, 60*time.Second, common.FlagDescTimeout)
 	cmd.Flags().StringVarP(&cmdFlags.Output, common.FlagNameOutput, "o", "", common.FlagDescOutput)
diff --git a/internal/cmd/skupper/listener/kube/listener_create.go b/internal/cmd/skupper/listener/kube/listener_create.go
index 5c1bff02..07b947c9 100644
--- a/internal/cmd/skupper/listener/kube/listener_create.go
+++ b/internal/cmd/skupper/listener/kube/listener_create.go
@@ -20,19 +20,19 @@ import (
 )
 
 type CmdListenerCreate struct {
-	client       skupperv2alpha1.SkupperV2alpha1Interface
-	CobraCmd     *cobra.Command
-	Flags        *common.CommandListenerCreateFlags
-	namespace    string
-	name         string
-	port         int
-	host         string
-	tlsSecret    string
-	listenerType string
-	routingKey   string
-	timeout      time.Duration
-	output       string
-	KubeClient   kubernetes.Interface
+	client         skupperv2alpha1.SkupperV2alpha1Interface
+	CobraCmd       *cobra.Command
+	Flags          *common.CommandListenerCreateFlags
+	namespace      string
+	name           string
+	port           int
+	host           string
+	tlsCredentials string
+	listenerType   string
+	routingKey     string
+	timeout        time.Duration
+	output         string
+	KubeClient     kubernetes.Interface
 }
 
 func NewCmdListenerCreate() *CmdListenerCreate {
@@ -101,9 +101,9 @@ func (cmd *CmdListenerCreate) ValidateInput(args []string) []error {
 		}
 	}
 
-	if cmd.Flags != nil && cmd.Flags.TlsSecret != "" {
+	if cmd.Flags != nil && cmd.Flags.TlsCredentials != "" {
 		// check that the secret exists
-		_, err := cmd.KubeClient.CoreV1().Secrets(cmd.namespace).Get(context.TODO(), cmd.Flags.TlsSecret, metav1.GetOptions{})
+		_, err := cmd.KubeClient.CoreV1().Secrets(cmd.namespace).Get(context.TODO(), cmd.Flags.TlsCredentials, metav1.GetOptions{})
 		if err != nil {
 			validationErrors = append(validationErrors, fmt.Errorf("tls-secret is not valid: does not exist"))
 		}
@@ -145,7 +145,7 @@ func (cmd *CmdListenerCreate) InputToOptions() {
 		cmd.routingKey = cmd.Flags.RoutingKey
 	}
 	cmd.timeout = cmd.Flags.Timeout
-	cmd.tlsSecret = cmd.Flags.TlsSecret
+	cmd.tlsCredentials = cmd.Flags.TlsCredentials
 	cmd.listenerType = cmd.Flags.ListenerType
 	cmd.output = cmd.Flags.Output
 }
@@ -165,7 +165,7 @@ func (cmd *CmdListenerCreate) Run() error {
 			Host:           cmd.host,
 			Port:           cmd.port,
 			RoutingKey:     cmd.routingKey,
-			TlsCredentials: cmd.tlsSecret,
+			TlsCredentials: cmd.tlsCredentials,
 			Type:           cmd.listenerType,
 		},
 	}
diff --git a/internal/cmd/skupper/listener/kube/listener_update.go b/internal/cmd/skupper/listener/kube/listener_update.go
index 696cf1d1..abb576df 100644
--- a/internal/cmd/skupper/listener/kube/listener_update.go
+++ b/internal/cmd/skupper/listener/kube/listener_update.go
@@ -19,13 +19,13 @@ import (
 )
 
 type ListenerUpdates struct {
-	routingKey   string
-	host         string
-	tlsSecret    string
-	listenerType string
-	port         int
-	timeout      time.Duration
-	output       string
+	routingKey     string
+	host           string
+	tlsCredentials string
+	listenerType   string
+	port           int
+	timeout        time.Duration
+	output         string
 }
 type CmdListenerUpdate struct {
 	client          skupperv2alpha1.SkupperV2alpha1Interface
@@ -86,7 +86,7 @@ func (cmd *CmdListenerUpdate) ValidateInput(args []string) []error {
 			cmd.resourceVersion = listener.ResourceVersion
 			cmd.newSettings.host = listener.Spec.Host
 			cmd.newSettings.port = listener.Spec.Port
-			cmd.newSettings.tlsSecret = listener.Spec.TlsCredentials
+			cmd.newSettings.tlsCredentials = listener.Spec.TlsCredentials
 			cmd.newSettings.listenerType = listener.Spec.Type
 		}
 	}
@@ -104,12 +104,12 @@ func (cmd *CmdListenerUpdate) ValidateInput(args []string) []error {
 	if cmd.Flags != nil && cmd.Flags.Host != "" {
 		cmd.newSettings.host = cmd.Flags.Host
 	}
-	if cmd.Flags != nil && cmd.Flags.TlsSecret != "" {
-		_, err := cmd.KubeClient.CoreV1().Secrets(cmd.namespace).Get(context.TODO(), cmd.Flags.TlsSecret, metav1.GetOptions{})
+	if cmd.Flags != nil && cmd.Flags.TlsCredentials != "" {
+		_, err := cmd.KubeClient.CoreV1().Secrets(cmd.namespace).Get(context.TODO(), cmd.Flags.TlsCredentials, metav1.GetOptions{})
 		if err != nil {
 			validationErrors = append(validationErrors, fmt.Errorf("tls-secret is not valid: does not exist"))
 		} else {
-			cmd.newSettings.tlsSecret = cmd.Flags.TlsSecret
+			cmd.newSettings.tlsCredentials = cmd.Flags.TlsCredentials
 		}
 	}
 	if cmd.Flags != nil && cmd.Flags.ListenerType != "" {
@@ -161,7 +161,7 @@ func (cmd *CmdListenerUpdate) Run() error {
 			Host:           cmd.newSettings.host,
 			Port:           cmd.newSettings.port,
 			RoutingKey:     cmd.newSettings.routingKey,
-			TlsCredentials: cmd.newSettings.tlsSecret,
+			TlsCredentials: cmd.newSettings.tlsCredentials,
 			Type:           cmd.newSettings.listenerType,
 		},
 	}
diff --git a/internal/cmd/skupper/listener/listener.go b/internal/cmd/skupper/listener/listener.go
index 5dd52088..bef9aa7c 100644
--- a/internal/cmd/skupper/listener/listener.go
+++ b/internal/cmd/skupper/listener/listener.go
@@ -45,7 +45,7 @@ func CmdListenerCreateFactory(configuredPlatform types.Platform) *cobra.Command
 
 	cmd.Flags().StringVarP(&cmdFlags.RoutingKey, common.FlagNameRoutingKey, "r", "", common.FlagDescRoutingKey)
 	cmd.Flags().StringVar(&cmdFlags.Host, common.FlagNameListenerHost, "", common.FlagDescListenerHost)
-	cmd.Flags().StringVarP(&cmdFlags.TlsSecret, common.FlagNameTlsSecret, "t", "", common.FlagDescTlsSecret)
+	cmd.Flags().StringVarP(&cmdFlags.TlsCredentials, common.FlagNameTlsCredentials, "t", "", common.FlagDescTlsCredentials)
 	cmd.Flags().StringVar(&cmdFlags.ListenerType, common.FlagNameListenerType, "tcp", common.FlagDescListenerType)
 	cmd.Flags().DurationVar(&cmdFlags.Timeout, common.FlagNameTimeout, 60*time.Second, common.FlagDescTimeout)
 	cmd.Flags().StringVarP(&cmdFlags.Output, common.FlagNameOutput, "o", "", common.FlagDescOutput)
@@ -76,7 +76,7 @@ func CmdListenerUpdateFactory(configuredPlatform types.Platform) *cobra.Command
 
 	cmd.Flags().StringVarP(&cmdFlags.RoutingKey, common.FlagNameRoutingKey, "r", "", common.FlagDescRoutingKey)
 	cmd.Flags().StringVar(&cmdFlags.Host, common.FlagNameListenerHost, "", common.FlagDescListenerHost)
-	cmd.Flags().StringVarP(&cmdFlags.TlsSecret, common.FlagNameTlsSecret, "t", "", common.FlagDescTlsSecret)
+	cmd.Flags().StringVarP(&cmdFlags.TlsCredentials, common.FlagNameTlsCredentials, "t", "", common.FlagDescTlsCredentials)
 	cmd.Flags().StringVar(&cmdFlags.ListenerType, common.FlagNameListenerType, "tcp", common.FlagDescListenerType)
 	cmd.Flags().DurationVar(&cmdFlags.Timeout, common.FlagNameTimeout, 60*time.Second, common.FlagDescTimeout)
 	cmd.Flags().IntVar(&cmdFlags.Port, common.FlagNameListenerPort, 0, common.FlagDescListenerPort)


Test patch:
diff --git a/cmd/network-console-collector/internal/collector/metrics/metrics_test.go b/cmd/network-console-collector/internal/collector/metrics/metrics_test.go
new file mode 100644
index 00000000..7aa35864
--- /dev/null
+++ b/cmd/network-console-collector/internal/collector/metrics/metrics_test.go
@@ -0,0 +1,235 @@
+package metrics
+
+import (
+	"testing"
+
+	"github.com/prometheus/client_golang/prometheus"
+	prom_testutil "github.com/prometheus/client_golang/prometheus/testutil"
+	"github.com/skupperproject/skupper/pkg/vanflow"
+	"gotest.tools/assert"
+)
+
+func TestSiteInfoMetrics(t *testing.T) {
+	reg := prometheus.NewPedanticRegistry()
+	metrics := New(reg)
+
+	metrics.Add(vanflow.SiteRecord{BaseRecord: vanflow.NewBase("s1"), Name: ptrTo("x")})
+	metrics.Update(vanflow.SiteRecord{}, vanflow.SiteRecord{BaseRecord: vanflow.NewBase("s1"), Name: ptrTo("x"), Version: ptrTo("1.0")})
+	assert.Equal(t, prom_testutil.CollectAndCount(reg, "skupper_site_info"), 1)
+	assert.Equal(t, prom_testutil.ToFloat64(metrics.siteInfo.WithLabelValues("s1", "x", "1.0")), 1.0)
+	metrics.Remove(vanflow.SiteRecord{BaseRecord: vanflow.NewBase("s1"), Name: ptrTo("x"), Version: ptrTo("1.0")})
+	assert.Equal(t, prom_testutil.ToFloat64(metrics.siteInfo.WithLabelValues("s1", "x", "1.0")), 0.0)
+}
+
+func TestSiteRouterMetrics(t *testing.T) {
+	reg := prometheus.NewPedanticRegistry()
+	metrics := New(reg)
+
+	metrics.Add(vanflow.RouterRecord{BaseRecord: vanflow.NewBase("r1"), Mode: ptrTo("inter-router"), Parent: ptrTo("s1")})
+	metrics.Add(vanflow.RouterRecord{BaseRecord: vanflow.NewBase("r2"), Parent: ptrTo("s1")})
+	metrics.Update(vanflow.RouterRecord{}, vanflow.RouterRecord{BaseRecord: vanflow.NewBase("r2"), Mode: ptrTo("inter-router"), Parent: ptrTo("s1")})
+	metrics.Add(vanflow.RouterRecord{BaseRecord: vanflow.NewBase("r3"), Mode: ptrTo("edge"), Parent: ptrTo("s1")})
+	assert.Equal(t, prom_testutil.CollectAndCount(reg, "skupper_routers_total"), 2)
+	assert.Equal(t, prom_testutil.ToFloat64(metrics.routerInfo.WithLabelValues("s1", "inter-router")), 2.0)
+	assert.Equal(t, prom_testutil.ToFloat64(metrics.routerInfo.WithLabelValues("s1", "edge")), 1.0)
+	metrics.Remove(vanflow.RouterRecord{BaseRecord: vanflow.NewBase("r3"), Mode: ptrTo("edge"), Parent: ptrTo("s1")})
+	assert.Equal(t, prom_testutil.CollectAndCount(reg, "skupper_routers_total"), 2)
+	assert.Equal(t, prom_testutil.ToFloat64(metrics.routerInfo.WithLabelValues("s1", "inter-router")), 2.0)
+	assert.Equal(t, prom_testutil.ToFloat64(metrics.routerInfo.WithLabelValues("s1", "edge")), 0.0)
+}
+
+func TestSiteLinkMetrics(t *testing.T) {
+	reg := prometheus.NewPedanticRegistry()
+	metrics := New(reg)
+	interRouter := ptrTo("inter-router")
+	statusUp := ptrTo("up")
+	statusDown := ptrTo("down")
+
+	metrics.Add(vanflow.LinkRecord{
+		BaseRecord: vanflow.NewBase("l01"),
+		Role:       interRouter,
+		Status:     statusDown,
+		Parent:     ptrTo("r01"),
+	})
+	metrics.Add(vanflow.LinkRecord{
+		BaseRecord: vanflow.NewBase("l02"),
+		Role:       interRouter,
+		Status:     statusUp,
+		Peer:       ptrTo("ap01"),
+		Parent:     ptrTo("r01"),
+	})
+	assert.Equal(t, prom_testutil.CollectAndCount(reg, "skupper_site_links_total"), 0)
+	metrics.Add(vanflow.RouterRecord{
+		BaseRecord: vanflow.NewBase("r01"),
+	})
+	assert.Equal(t, prom_testutil.CollectAndCount(reg, "skupper_site_links_total"), 0)
+	metrics.Update(vanflow.RouterRecord{
+		BaseRecord: vanflow.NewBase("r01"),
+	}, vanflow.RouterRecord{
+		BaseRecord: vanflow.NewBase("r01"), Parent: ptrTo("s01"), Mode: interRouter,
+	})
+	assert.Equal(t, prom_testutil.CollectAndCount(reg, "skupper_site_links_total"), 2)
+	assert.Equal(t, prom_testutil.ToFloat64(metrics.siteLinkInfo.WithLabelValues("s01", "inter-router", "down")), 1.0)
+	assert.Equal(t, prom_testutil.ToFloat64(metrics.siteLinkInfo.WithLabelValues("s01", "inter-router", "up")), 1.0)
+	metrics.Update(
+		vanflow.LinkRecord{
+			BaseRecord: vanflow.NewBase("l01"),
+			Role:       interRouter,
+			Status:     statusDown,
+			Parent:     ptrTo("r01"),
+		}, vanflow.LinkRecord{
+			BaseRecord: vanflow.NewBase("l01"),
+			Role:       interRouter,
+			Status:     statusUp,
+			Peer:       ptrTo("ap02"),
+			Parent:     ptrTo("r01"),
+		},
+	)
+	assert.Equal(t, prom_testutil.CollectAndCount(reg, "skupper_site_links_total"), 2)
+	assert.Equal(t, prom_testutil.ToFloat64(metrics.siteLinkInfo.WithLabelValues("s01", "inter-router", "down")), 0.0)
+	assert.Equal(t, prom_testutil.ToFloat64(metrics.siteLinkInfo.WithLabelValues("s01", "inter-router", "up")), 2.0)
+
+	metrics.Update(vanflow.LinkRecord{}, vanflow.LinkRecord{
+		BaseRecord: vanflow.NewBase("l03"),
+		Role:       interRouter,
+		Status:     statusUp,
+		Peer:       ptrTo("ap01"),
+		Parent:     ptrTo("r02"),
+	})
+	assert.Equal(t, prom_testutil.CollectAndCount(reg, "skupper_site_links_total"), 2)
+	metrics.Remove(vanflow.LinkRecord{
+		BaseRecord: vanflow.NewBase("l03"),
+		Role:       interRouter,
+		Status:     statusUp,
+		Peer:       ptrTo("ap01"),
+		Parent:     ptrTo("r02"),
+	})
+	assert.Equal(t, prom_testutil.CollectAndCount(reg, "skupper_site_links_total"), 2)
+	metrics.Add(vanflow.RouterRecord{
+		BaseRecord: vanflow.NewBase("r02"), Parent: ptrTo("s02"), Mode: interRouter,
+	})
+	assert.Equal(t, prom_testutil.CollectAndCount(reg, "skupper_site_links_total"), 2)
+}
+
+func TestSiteListenerConnectorMetrics(t *testing.T) {
+	reg := prometheus.NewPedanticRegistry()
+	metrics := New(reg)
+	interRouter := ptrTo("inter-router")
+
+	metrics.Add(vanflow.ListenerRecord{
+		BaseRecord: vanflow.NewBase("l01"),
+		Parent:     ptrTo("r01"),
+	})
+	metrics.Add(vanflow.ListenerRecord{
+		BaseRecord: vanflow.NewBase("l02"),
+		Parent:     ptrTo("r01"),
+	})
+	metrics.Add(vanflow.ConnectorRecord{
+		BaseRecord: vanflow.NewBase("c01"),
+		Parent:     ptrTo("r01"),
+	})
+	assert.Equal(t, prom_testutil.CollectAndCount(reg, "skupper_site_listeners_total"), 0)
+	assert.Equal(t, prom_testutil.CollectAndCount(reg, "skupper_site_connectors_total"), 0)
+	metrics.Add(vanflow.RouterRecord{
+		BaseRecord: vanflow.NewBase("r01"),
+	})
+	assert.Equal(t, prom_testutil.CollectAndCount(reg, "skupper_site_listeners_total"), 0)
+	assert.Equal(t, prom_testutil.CollectAndCount(reg, "skupper_site_connectors_total"), 0)
+	metrics.Update(vanflow.RouterRecord{
+		BaseRecord: vanflow.NewBase("r01"),
+	}, vanflow.RouterRecord{
+		BaseRecord: vanflow.NewBase("r01"), Parent: ptrTo("s01"), Mode: interRouter,
+	})
+	assert.Equal(t, prom_testutil.CollectAndCount(reg, "skupper_site_listeners_total"), 1)
+	assert.Equal(t, prom_testutil.CollectAndCount(reg, "skupper_site_connectors_total"), 1)
+	assert.Equal(t, prom_testutil.ToFloat64(metrics.siteListenerInfo.WithLabelValues("s01")), 2.0)
+	assert.Equal(t, prom_testutil.ToFloat64(metrics.siteConnectorInfo.WithLabelValues("s01")), 1.0)
+	metrics.Remove(vanflow.ListenerRecord{
+		BaseRecord: vanflow.NewBase("l02"),
+		Parent:     ptrTo("r01"),
+	})
+	assert.Equal(t, prom_testutil.ToFloat64(metrics.siteListenerInfo.WithLabelValues("s01")), 1.0)
+	metrics.Remove(vanflow.ConnectorRecord{
+		BaseRecord: vanflow.NewBase("c01"),
+		Parent:     ptrTo("r01"),
+	})
+	assert.Equal(t, prom_testutil.ToFloat64(metrics.siteConnectorInfo.WithLabelValues("s01")), 0.0)
+}
+
+func TestSiteLinkErrorMetrics(t *testing.T) {
+	reg := prometheus.NewPedanticRegistry()
+	metrics := New(reg)
+	interRouter := ptrTo("inter-router")
+	statusDown := ptrTo("down")
+
+	metrics.Add(vanflow.RouterRecord{
+		BaseRecord: vanflow.NewBase("r01"), Parent: ptrTo("s01"), Mode: interRouter,
+	})
+	metrics.Add(vanflow.LinkRecord{
+		BaseRecord: vanflow.NewBase("l01"),
+		Role:       interRouter,
+		Status:     statusDown,
+		Parent:     ptrTo("r01"),
+		DownCount:  ptrTo(uint64(12)),
+	})
+	assert.Equal(t, prom_testutil.CollectAndCount(reg, "skupper_site_link_errors_total"), 1)
+	assert.Equal(t, prom_testutil.ToFloat64(metrics.siteLinkErrors.WithLabelValues("s01", "inter-router")), 0.0)
+	metrics.Update(
+		vanflow.LinkRecord{
+			BaseRecord: vanflow.NewBase("l01"),
+			Role:       interRouter,
+			Status:     statusDown,
+			Parent:     ptrTo("r01"),
+			DownCount:  ptrTo(uint64(12)),
+		}, vanflow.LinkRecord{
+			BaseRecord: vanflow.NewBase("l01"),
+			Role:       interRouter,
+			Status:     statusDown,
+			Peer:       ptrTo("ap02"),
+			Parent:     ptrTo("r01"),
+			DownCount:  ptrTo(uint64(15)),
+		},
+	)
+	assert.Equal(t, prom_testutil.CollectAndCount(reg, "skupper_site_link_errors_total"), 1)
+	assert.Equal(t, prom_testutil.ToFloat64(metrics.siteLinkErrors.WithLabelValues("s01", "inter-router")), 3.0)
+	metrics.Remove(vanflow.LinkRecord{
+		BaseRecord: vanflow.NewBase("l01"),
+		Role:       interRouter,
+		Status:     statusDown,
+		Parent:     ptrTo("r01"),
+		DownCount:  ptrTo(uint64(15)),
+	})
+	// cannot decrease
+	assert.Equal(t, prom_testutil.CollectAndCount(reg, "skupper_site_link_errors_total"), 1)
+	assert.Equal(t, prom_testutil.ToFloat64(metrics.siteLinkErrors.WithLabelValues("s01", "inter-router")), 3.0)
+	metrics.Add(vanflow.LinkRecord{
+		BaseRecord: vanflow.NewBase("l01"),
+		Role:       interRouter,
+		Status:     statusDown,
+		Parent:     ptrTo("r01"),
+		DownCount:  ptrTo(uint64(0)),
+	})
+	metrics.Update(
+		vanflow.LinkRecord{
+			BaseRecord: vanflow.NewBase("l01"),
+			Role:       interRouter,
+			Status:     statusDown,
+			Parent:     ptrTo("r01"),
+			DownCount:  ptrTo(uint64(0)),
+		}, vanflow.LinkRecord{
+			BaseRecord: vanflow.NewBase("l01"),
+			Role:       interRouter,
+			Status:     statusDown,
+			Peer:       ptrTo("ap02"),
+			Parent:     ptrTo("r01"),
+			DownCount:  ptrTo(uint64(1)),
+		},
+	)
+	// after delete, can still increment
+	assert.Equal(t, prom_testutil.CollectAndCount(reg, "skupper_site_link_errors_total"), 1)
+	assert.Equal(t, prom_testutil.ToFloat64(metrics.siteLinkErrors.WithLabelValues("s01", "inter-router")), 4.0)
+}
+
+func ptrTo[T any](s T) *T {
+	return &s
+}
diff --git a/cmd/network-console-collector/internal/server/shared_test.go b/cmd/network-console-collector/internal/server/shared_test.go
index a5223326..aa5aa482 100644
--- a/cmd/network-console-collector/internal/server/shared_test.go
+++ b/cmd/network-console-collector/internal/server/shared_test.go
@@ -49,10 +49,6 @@ type reset interface {
 	Reindex(vanflow.Record)
 }
 
-type statusCoder interface {
-	StatusCode() int
-}
-
 func withParameters(params map[string][]string) func(context.Context, *http.Request) error {
 	return func(ctx context.Context, r *http.Request) error {
 		values := r.URL.Query()
diff --git a/internal/cmd/skupper/connector/connector_test.go b/internal/cmd/skupper/connector/connector_test.go
index 854c8a9c..432c0de7 100644
--- a/internal/cmd/skupper/connector/connector_test.go
+++ b/internal/cmd/skupper/connector/connector_test.go
@@ -24,7 +24,7 @@ func TestCmdConnectorFactory(t *testing.T) {
 			expectedFlagsWithDefaultValue: map[string]interface{}{
 				common.FlagNameRoutingKey:      "",
 				common.FlagNameHost:            "",
-				common.FlagNameTlsSecret:       "",
+				common.FlagNameTlsCredentials:  "",
 				common.FlagNameConnectorType:   "tcp",
 				common.FlagNameIncludeNotReady: "false",
 				common.FlagNameSelector:        "",
@@ -39,7 +39,7 @@ func TestCmdConnectorFactory(t *testing.T) {
 			expectedFlagsWithDefaultValue: map[string]interface{}{
 				common.FlagNameRoutingKey:      "",
 				common.FlagNameHost:            "",
-				common.FlagNameTlsSecret:       "",
+				common.FlagNameTlsCredentials:  "",
 				common.FlagNameConnectorType:   "tcp",
 				common.FlagNameIncludeNotReady: "false",
 				common.FlagNameSelector:        "",
diff --git a/internal/cmd/skupper/connector/kube/connector_create_test.go b/internal/cmd/skupper/connector/kube/connector_create_test.go
index db361cf7..42e8d2bd 100644
--- a/internal/cmd/skupper/connector/kube/connector_create_test.go
+++ b/internal/cmd/skupper/connector/kube/connector_create_test.go
@@ -166,9 +166,9 @@ func TestCmdConnectorCreate_ValidateInput(t *testing.T) {
 			name: "tls-secret does not exist",
 			args: []string{"my-connector-tls", "8080"},
 			flags: common.CommandConnectorCreateFlags{
-				TlsSecret: "not-valid",
-				Timeout:   1 * time.Minute,
-				Selector:  "backend",
+				TlsCredentials: "not-valid",
+				Timeout:        1 * time.Minute,
+				Selector:       "backend",
 			},
 			expectedErrors: []string{"tls-secret is not valid: does not exist"},
 		},
@@ -565,51 +565,51 @@ func TestCmdConnectorCreate_ValidateWorkload(t *testing.T) {
 func TestCmdConnectorCreate_InputToOptions(t *testing.T) {
 
 	type test struct {
-		name                  string
-		flags                 common.CommandConnectorCreateFlags
-		Connectorname         string
-		expectedTlsSecret     string
-		expectedHost          string
-		expectedSelector      string
-		expectedRoutingKey    string
-		expectedConnectorType string
-		expectedOutput        string
-		expectedTimeout       time.Duration
+		name                   string
+		flags                  common.CommandConnectorCreateFlags
+		Connectorname          string
+		expectedTlsCredentials string
+		expectedHost           string
+		expectedSelector       string
+		expectedRoutingKey     string
+		expectedConnectorType  string
+		expectedOutput         string
+		expectedTimeout        time.Duration
 	}
 
 	testTable := []test{
 		{
-			name:                  "test1",
-			flags:                 common.CommandConnectorCreateFlags{"backend", "", "app=backend", "secret", "tcp", true, "", 20 * time.Second, "json"},
-			expectedTlsSecret:     "secret",
-			expectedHost:          "",
-			expectedRoutingKey:    "backend",
-			expectedTimeout:       20 * time.Second,
-			expectedConnectorType: "tcp",
-			expectedOutput:        "json",
-			expectedSelector:      "app=backend",
-		},
-		{
-			name:                  "test2",
-			flags:                 common.CommandConnectorCreateFlags{"backend", "backend", "", "secret", "tcp", true, "", 20 * time.Second, "json"},
-			expectedTlsSecret:     "secret",
-			expectedHost:          "backend",
-			expectedRoutingKey:    "backend",
-			expectedTimeout:       20 * time.Second,
-			expectedConnectorType: "tcp",
-			expectedOutput:        "json",
-			expectedSelector:      "",
-		},
-		{
-			name:                  "test3",
-			flags:                 common.CommandConnectorCreateFlags{"", "", "", "secret", "tcp", false, "", 30 * time.Second, "yaml"},
-			expectedTlsSecret:     "secret",
-			expectedHost:          "",
-			expectedRoutingKey:    "test3",
-			expectedTimeout:       30 * time.Second,
-			expectedConnectorType: "tcp",
-			expectedOutput:        "yaml",
-			expectedSelector:      "app=test3",
+			name:                   "test1",
+			flags:                  common.CommandConnectorCreateFlags{"backend", "", "app=backend", "secret", "tcp", true, "", 20 * time.Second, "json"},
+			expectedTlsCredentials: "secret",
+			expectedHost:           "",
+			expectedRoutingKey:     "backend",
+			expectedTimeout:        20 * time.Second,
+			expectedConnectorType:  "tcp",
+			expectedOutput:         "json",
+			expectedSelector:       "app=backend",
+		},
+		{
+			name:                   "test2",
+			flags:                  common.CommandConnectorCreateFlags{"backend", "backend", "", "secret", "tcp", true, "", 20 * time.Second, "json"},
+			expectedTlsCredentials: "secret",
+			expectedHost:           "backend",
+			expectedRoutingKey:     "backend",
+			expectedTimeout:        20 * time.Second,
+			expectedConnectorType:  "tcp",
+			expectedOutput:         "json",
+			expectedSelector:       "",
+		},
+		{
+			name:                   "test3",
+			flags:                  common.CommandConnectorCreateFlags{"", "", "", "secret", "tcp", false, "", 30 * time.Second, "yaml"},
+			expectedTlsCredentials: "secret",
+			expectedHost:           "",
+			expectedRoutingKey:     "test3",
+			expectedTimeout:        30 * time.Second,
+			expectedConnectorType:  "tcp",
+			expectedOutput:         "yaml",
+			expectedSelector:       "app=test3",
 		},
 	}
 
@@ -626,7 +626,7 @@ func TestCmdConnectorCreate_InputToOptions(t *testing.T) {
 
 			assert.Check(t, cmd.routingKey == test.expectedRoutingKey)
 			assert.Check(t, cmd.output == test.expectedOutput)
-			assert.Check(t, cmd.tlsSecret == test.expectedTlsSecret)
+			assert.Check(t, cmd.tlsCredentials == test.expectedTlsCredentials)
 			assert.Check(t, cmd.host == test.expectedHost)
 			assert.Check(t, cmd.timeout == test.expectedTimeout)
 			assert.Check(t, cmd.selector == test.expectedSelector)
@@ -655,7 +655,7 @@ func TestCmdConnectorCreate_Run(t *testing.T) {
 			flags: common.CommandConnectorCreateFlags{
 				ConnectorType:   "tcp",
 				RoutingKey:      "keyname",
-				TlsSecret:       "secretname",
+				TlsCredentials:  "secretname",
 				IncludeNotReady: true,
 				Selector:        "app=backend",
 				Timeout:         10 * time.Second,
@@ -669,7 +669,7 @@ func TestCmdConnectorCreate_Run(t *testing.T) {
 				ConnectorType:   "tcp",
 				Host:            "hostname",
 				RoutingKey:      "keyname",
-				TlsSecret:       "secretname",
+				TlsCredentials:  "secretname",
 				IncludeNotReady: true,
 				Timeout:         10 * time.Second,
 				Output:          "json",
diff --git a/internal/cmd/skupper/connector/kube/connector_update_test.go b/internal/cmd/skupper/connector/kube/connector_update_test.go
index 8b982ff6..dc3c5eed 100644
--- a/internal/cmd/skupper/connector/kube/connector_update_test.go
+++ b/internal/cmd/skupper/connector/kube/connector_update_test.go
@@ -117,8 +117,8 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 			name: "tls-secret is not valid",
 			args: []string{"my-connector-tls"},
 			flags: common.CommandConnectorUpdateFlags{
-				TlsSecret: "test-tls",
-				Timeout:   50 * time.Second,
+				TlsCredentials: "test-tls",
+				Timeout:        50 * time.Second,
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.Connector{
@@ -365,7 +365,7 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 			args: []string{"my-connector-flags"},
 			flags: common.CommandConnectorUpdateFlags{
 				RoutingKey:      "routingkeyname",
-				TlsSecret:       "secretname",
+				TlsCredentials:  "secretname",
 				Port:            1234,
 				ConnectorType:   "tcp",
 				IncludeNotReady: false,
@@ -942,7 +942,7 @@ func TestCmdConnectorUpdate_Run(t *testing.T) {
 				connectorType:   "tcp",
 				host:            "hostname",
 				routingKey:      "keyname",
-				tlsSecret:       "secretname",
+				tlsCredentials:  "secretname",
 				includeNotReady: true,
 				workload:        "deployment/backend",
 				selector:        "backend",
@@ -975,7 +975,7 @@ func TestCmdConnectorUpdate_Run(t *testing.T) {
 				connectorType:   "tcp",
 				host:            "hostname",
 				routingKey:      "keyname",
-				tlsSecret:       "secretname",
+				tlsCredentials:  "secretname",
 				includeNotReady: true,
 				workload:        "deployment/backend",
 				selector:        "backend",
diff --git a/internal/cmd/skupper/connector/nonkube/connector_create_test.go b/internal/cmd/skupper/connector/nonkube/connector_create_test.go
index 7b407551..228b8707 100644
--- a/internal/cmd/skupper/connector/nonkube/connector_create_test.go
+++ b/internal/cmd/skupper/connector/nonkube/connector_create_test.go
@@ -110,11 +110,11 @@ func TestNonKubeCmdConnectorCreate_ValidateInput(t *testing.T) {
 			name: "flags all valid",
 			args: []string{"my-connector-flags", "8080"},
 			flags: &common.CommandConnectorCreateFlags{
-				RoutingKey:    "routingkeyname",
-				TlsSecret:     "secretname",
-				ConnectorType: "tcp",
-				Output:        "json",
-				Host:          "1.2.3.4",
+				RoutingKey:     "routingkeyname",
+				TlsCredentials: "secretname",
+				ConnectorType:  "tcp",
+				Output:         "json",
+				Host:           "1.2.3.4",
 			},
 			expectedErrors: []string{},
 		},
@@ -148,51 +148,51 @@ func TestNonKubeCmdConnectorCreate_ValidateInput(t *testing.T) {
 func TestNonKubeCmdConnectorCreate_InputToOptions(t *testing.T) {
 
 	type test struct {
-		name                  string
-		args                  []string
-		namespace             string
-		flags                 common.CommandConnectorCreateFlags
-		expectedNamespace     string
-		Connectorname         string
-		expectedTlsSecret     string
-		expectedHost          string
-		expectedRoutingKey    string
-		expectedConnectorType string
-		expectedOutput        string
+		name                   string
+		args                   []string
+		namespace              string
+		flags                  common.CommandConnectorCreateFlags
+		expectedNamespace      string
+		Connectorname          string
+		expectedTlsCredentials string
+		expectedHost           string
+		expectedRoutingKey     string
+		expectedConnectorType  string
+		expectedOutput         string
 	}
 
 	testTable := []test{
 		{
-			name:                  "test1",
-			flags:                 common.CommandConnectorCreateFlags{"backend", "", "", "secret", "tcp", false, "", 0, "json"},
-			expectedTlsSecret:     "secret",
-			expectedHost:          "",
-			expectedRoutingKey:    "backend",
-			expectedConnectorType: "tcp",
-			expectedOutput:        "json",
-			expectedNamespace:     "default",
+			name:                   "test1",
+			flags:                  common.CommandConnectorCreateFlags{"backend", "", "", "secret", "tcp", false, "", 0, "json"},
+			expectedTlsCredentials: "secret",
+			expectedHost:           "",
+			expectedRoutingKey:     "backend",
+			expectedConnectorType:  "tcp",
+			expectedOutput:         "json",
+			expectedNamespace:      "default",
 		},
 		{
-			name:                  "test2",
-			namespace:             "test",
-			flags:                 common.CommandConnectorCreateFlags{"backend", "1.2.3.4", "", "secret", "tcp", false, "", 0, "json"},
-			expectedTlsSecret:     "secret",
-			expectedHost:          "1.2.3.4",
-			expectedRoutingKey:    "backend",
-			expectedConnectorType: "tcp",
-			expectedOutput:        "json",
-			expectedNamespace:     "test",
+			name:                   "test2",
+			namespace:              "test",
+			flags:                  common.CommandConnectorCreateFlags{"backend", "1.2.3.4", "", "secret", "tcp", false, "", 0, "json"},
+			expectedTlsCredentials: "secret",
+			expectedHost:           "1.2.3.4",
+			expectedRoutingKey:     "backend",
+			expectedConnectorType:  "tcp",
+			expectedOutput:         "json",
+			expectedNamespace:      "test",
 		},
 		{
-			name:                  "test3",
-			namespace:             "test",
-			flags:                 common.CommandConnectorCreateFlags{"", "", "", "secret", "tcp", false, "", 0, "yaml"},
-			expectedTlsSecret:     "secret",
-			expectedHost:          "",
-			expectedRoutingKey:    "my-Connector",
-			expectedConnectorType: "tcp",
-			expectedOutput:        "yaml",
-			expectedNamespace:     "test",
+			name:                   "test3",
+			namespace:              "test",
+			flags:                  common.CommandConnectorCreateFlags{"", "", "", "secret", "tcp", false, "", 0, "yaml"},
+			expectedTlsCredentials: "secret",
+			expectedHost:           "",
+			expectedRoutingKey:     "my-Connector",
+			expectedConnectorType:  "tcp",
+			expectedOutput:         "yaml",
+			expectedNamespace:      "test",
 		},
 	}
 
@@ -207,7 +207,7 @@ func TestNonKubeCmdConnectorCreate_InputToOptions(t *testing.T) {
 			cmd.InputToOptions()
 
 			assert.Check(t, cmd.routingKey == test.expectedRoutingKey)
-			assert.Check(t, cmd.tlsSecret == test.expectedTlsSecret)
+			assert.Check(t, cmd.tlsCredentials == test.expectedTlsCredentials)
 			assert.Check(t, cmd.host == test.expectedHost)
 			assert.Check(t, cmd.connectorType == test.expectedConnectorType)
 			assert.Check(t, cmd.output == test.expectedOutput)
@@ -228,7 +228,7 @@ func TestNonKubeCmdConnectorCreate_Run(t *testing.T) {
 		output         string
 		errorMessage   string
 		routingKey     string
-		tlsSecret      string
+		tlsCredentials string
 		connectorType  string
 		connectorPort  int
 	}
@@ -243,7 +243,7 @@ func TestNonKubeCmdConnectorCreate_Run(t *testing.T) {
 			connectorPort:  8080,
 			connectorType:  "tcp",
 			routingKey:     "keyname",
-			tlsSecret:      "secretname",
+			tlsCredentials: "secretname",
 			host:           "1.2.3.4",
 		},
 		{
diff --git a/internal/cmd/skupper/connector/nonkube/connector_update_test.go b/internal/cmd/skupper/connector/nonkube/connector_update_test.go
index 8f0be13a..3250969a 100644
--- a/internal/cmd/skupper/connector/nonkube/connector_update_test.go
+++ b/internal/cmd/skupper/connector/nonkube/connector_update_test.go
@@ -105,12 +105,12 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 			name: "flags all valid",
 			args: []string{"my-connector"},
 			flags: &common.CommandConnectorUpdateFlags{
-				RoutingKey:    "routingkeyname",
-				TlsSecret:     "secretname",
-				Port:          1234,
-				ConnectorType: "tcp",
-				Output:        "json",
-				Host:          "1.2.3.4",
+				RoutingKey:     "routingkeyname",
+				TlsCredentials: "secretname",
+				Port:           1234,
+				ConnectorType:  "tcp",
+				Output:         "json",
+				Host:           "1.2.3.4",
 			},
 			expectedErrors: []string{},
 		},
@@ -174,31 +174,31 @@ func TestCmdConnectorUpdate_Run(t *testing.T) {
 		host                string
 		output              string
 		routingKey          string
-		tlsSecret           string
+		tlsCredentials      string
 		connectorType       string
 		port                int
 	}
 
 	testTable := []test{
 		{
-			name:          "runs ok",
-			namespace:     "test",
-			connectorName: "my-connector",
-			port:          8080,
-			connectorType: "tcp",
-			host:          "hostname",
-			routingKey:    "keyname",
-			tlsSecret:     "secretname",
+			name:           "runs ok",
+			namespace:      "test",
+			connectorName:  "my-connector",
+			port:           8080,
+			connectorType:  "tcp",
+			host:           "hostname",
+			routingKey:     "keyname",
+			tlsCredentials: "secretname",
 		},
 		{
-			name:          "run output json",
-			connectorName: "my-connector",
-			port:          8181,
-			connectorType: "tcp",
-			host:          "hostname",
-			routingKey:    "keyname",
-			tlsSecret:     "secretname",
-			output:        "json",
+			name:           "run output json",
+			connectorName:  "my-connector",
+			port:           8181,
+			connectorType:  "tcp",
+			host:           "hostname",
+			routingKey:     "keyname",
+			tlsCredentials: "secretname",
+			output:         "json",
 		},
 	}
 
@@ -210,7 +210,7 @@ func TestCmdConnectorUpdate_Run(t *testing.T) {
 		command.newSettings.port = test.port
 		command.newSettings.host = test.host
 		command.newSettings.routingKey = test.routingKey
-		command.newSettings.tlsSecret = test.tlsSecret
+		command.newSettings.tlsCredentials = test.tlsCredentials
 		command.namespace = test.namespace
 		command.connectorHandler = fs.NewConnectorHandler(command.namespace)
 		defer command.connectorHandler.Delete("my-connector")
diff --git a/internal/cmd/skupper/link/kube/link_generate_test.go b/internal/cmd/skupper/link/kube/link_generate_test.go
index e43ef8ba..a56f56bb 100644
--- a/internal/cmd/skupper/link/kube/link_generate_test.go
+++ b/internal/cmd/skupper/link/kube/link_generate_test.go
@@ -29,7 +29,7 @@ func TestCmdLinkGenerate_ValidateInput(t *testing.T) {
 		{
 			name:           "link yaml is not generated because there is no site in the namespace.",
 			expectedErrors: []string{"there is no skupper site in this namespace", "there is no active skupper site in this namespace"},
-			flags:          common.CommandLinkGenerateFlags{Cost: "1", TlsSecret: "secret", Timeout: time.Minute},
+			flags:          common.CommandLinkGenerateFlags{Cost: "1", TlsCredentials: "secret", Timeout: time.Minute},
 		},
 		{
 			name: "arguments were specified and they are not needed",
@@ -81,7 +81,7 @@ func TestCmdLinkGenerate_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			flags:          common.CommandLinkGenerateFlags{Cost: "1", TlsSecret: "secret", Timeout: time.Minute},
+			flags:          common.CommandLinkGenerateFlags{Cost: "1", TlsCredentials: "secret", Timeout: time.Minute},
 			expectedErrors: []string{"arguments are not allowed in this command"},
 		},
 		{
@@ -185,7 +185,7 @@ func TestCmdLinkGenerate_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			flags:          common.CommandLinkGenerateFlags{Cost: "one", TlsSecret: "secret", Timeout: time.Minute},
+			flags:          common.CommandLinkGenerateFlags{Cost: "one", TlsCredentials: "secret", Timeout: time.Minute},
 			expectedErrors: []string{"link cost is not valid: strconv.Atoi: parsing \"one\": invalid syntax"},
 		},
 		{
@@ -237,7 +237,7 @@ func TestCmdLinkGenerate_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			flags: common.CommandLinkGenerateFlags{Cost: "-4", TlsSecret: "secret", Timeout: time.Minute},
+			flags: common.CommandLinkGenerateFlags{Cost: "-4", TlsCredentials: "secret", Timeout: time.Minute},
 			expectedErrors: []string{
 				"link cost is not valid: value is not positive",
 			},
@@ -291,7 +291,7 @@ func TestCmdLinkGenerate_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			flags: common.CommandLinkGenerateFlags{Cost: "1", TlsSecret: "secret", Output: "not-valid", Timeout: time.Minute},
+			flags: common.CommandLinkGenerateFlags{Cost: "1", TlsCredentials: "secret", Output: "not-valid", Timeout: time.Minute},
 			expectedErrors: []string{
 				"output type is not valid: value not-valid not allowed. It should be one of this options: [json yaml]",
 			},
@@ -345,7 +345,7 @@ func TestCmdLinkGenerate_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			flags: common.CommandLinkGenerateFlags{Cost: "1", TlsSecret: "tls secret", Timeout: time.Minute},
+			flags: common.CommandLinkGenerateFlags{Cost: "1", TlsCredentials: "tls secret", Timeout: time.Minute},
 			expectedErrors: []string{
 				"the name of the tls secret is not valid: value does not match this regular expression: ^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$",
 			},
@@ -399,7 +399,7 @@ func TestCmdLinkGenerate_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			flags: common.CommandLinkGenerateFlags{Cost: "1", TlsSecret: "secret", Timeout: time.Second * 0},
+			flags: common.CommandLinkGenerateFlags{Cost: "1", TlsCredentials: "secret", Timeout: time.Second * 0},
 			expectedErrors: []string{
 				"timeout is not valid: duration must not be less than 10s; got 0s",
 			},
@@ -431,7 +431,7 @@ func TestCmdLinkGenerate_InputToOptions(t *testing.T) {
 		flags                       common.CommandLinkGenerateFlags
 		activeSite                  *v2alpha1.Site
 		expectedLinkname            string
-		expectedTlsSecret           string
+		expectedTlsCredentials      string
 		expectedCost                int
 		expectedOutput              string
 		expectedGenerateCredentials bool
@@ -442,7 +442,7 @@ func TestCmdLinkGenerate_InputToOptions(t *testing.T) {
 			name:                        "check options",
 			flags:                       common.CommandLinkGenerateFlags{"secret", "1", "json", false, time.Minute},
 			expectedCost:                1,
-			expectedTlsSecret:           "secret",
+			expectedTlsCredentials:      "secret",
 			expectedOutput:              "json",
 			expectedGenerateCredentials: false,
 		},
@@ -472,7 +472,7 @@ func TestCmdLinkGenerate_InputToOptions(t *testing.T) {
 			},
 			expectedLinkname:            "link-the-site",
 			expectedCost:                1,
-			expectedTlsSecret:           "link-the-site",
+			expectedTlsCredentials:      "link-the-site",
 			expectedOutput:              "json",
 			expectedGenerateCredentials: true,
 		},
@@ -491,7 +491,7 @@ func TestCmdLinkGenerate_InputToOptions(t *testing.T) {
 
 			assert.Check(t, cmd.linkName == test.expectedLinkname)
 			assert.Check(t, cmd.output == test.expectedOutput)
-			assert.Check(t, cmd.tlsSecret == test.expectedTlsSecret)
+			assert.Check(t, cmd.tlsCredentials == test.expectedTlsCredentials)
 			assert.Check(t, cmd.cost == test.expectedCost)
 			assert.Check(t, cmd.generateCredential == test.expectedGenerateCredentials)
 		})
@@ -512,7 +512,7 @@ func TestCmdLinkGenerate_Run(t *testing.T) {
 			setUpMock: func(command *CmdLinkGenerate) {
 				command.linkName = "my-link"
 				command.cost = 1
-				command.tlsSecret = "secret"
+				command.tlsCredentials = "secret"
 				command.output = "yaml"
 				command.generateCredential = true
 				command.activeSite = &v2alpha1.Site{
@@ -557,7 +557,7 @@ func TestCmdLinkGenerate_Run(t *testing.T) {
 			setUpMock: func(command *CmdLinkGenerate) {
 				command.linkName = "my-link"
 				command.cost = 1
-				command.tlsSecret = "secret"
+				command.tlsCredentials = "secret"
 				command.output = "yaml"
 				command.generateCredential = false
 				command.activeSite = &v2alpha1.Site{
@@ -602,7 +602,7 @@ func TestCmdLinkGenerate_Run(t *testing.T) {
 			setUpMock: func(command *CmdLinkGenerate) {
 				command.linkName = "my-link"
 				command.cost = 1
-				command.tlsSecret = "secret"
+				command.tlsCredentials = "secret"
 				command.output = ""
 				command.generateCredential = true
 				command.activeSite = &v2alpha1.Site{
@@ -648,7 +648,7 @@ func TestCmdLinkGenerate_Run(t *testing.T) {
 			setUpMock: func(command *CmdLinkGenerate) {
 				command.linkName = "my-link"
 				command.cost = 1
-				command.tlsSecret = "secret"
+				command.tlsCredentials = "secret"
 				command.output = "unsupported"
 				command.generateCredential = true
 				command.activeSite = &v2alpha1.Site{
@@ -694,7 +694,7 @@ func TestCmdLinkGenerate_Run(t *testing.T) {
 			setUpMock: func(command *CmdLinkGenerate) {
 				command.linkName = "my-link"
 				command.cost = 1
-				command.tlsSecret = "secret"
+				command.tlsCredentials = "secret"
 				command.output = "yaml"
 				command.generateCredential = false
 				command.activeSite = &v2alpha1.Site{
@@ -726,7 +726,7 @@ func TestCmdLinkGenerate_Run(t *testing.T) {
 			setUpMock: func(command *CmdLinkGenerate) {
 				command.linkName = "my-link"
 				command.cost = 1
-				command.tlsSecret = "secret"
+				command.tlsCredentials = "secret"
 				command.output = "yaml"
 				command.generateCredential = false
 				command.activeSite = nil
@@ -738,7 +738,7 @@ func TestCmdLinkGenerate_Run(t *testing.T) {
 			setUpMock: func(command *CmdLinkGenerate) {
 				command.linkName = "my-link"
 				command.cost = 1
-				command.tlsSecret = "secret"
+				command.tlsCredentials = "secret"
 				command.output = "yaml"
 				command.generateCredential = true
 				command.activeSite = &v2alpha1.Site{
@@ -806,7 +806,7 @@ func TestCmdLinkGenerate_WaitUntil(t *testing.T) {
 		generateCredential bool
 		generatedLink      v2alpha1.Link
 		outputType         string
-		tlsSecret          string
+		tlsCredentials     string
 		timeout            time.Duration
 		k8sObjects         []runtime.Object
 		skupperObjects     []runtime.Object
@@ -819,7 +819,7 @@ func TestCmdLinkGenerate_WaitUntil(t *testing.T) {
 			name:               "secret is not returned",
 			outputType:         "yaml",
 			timeout:            time.Second,
-			tlsSecret:          "linkSecret",
+			tlsCredentials:     "linkSecret",
 			generateCredential: true,
 			expectError:        true,
 		},
@@ -842,7 +842,7 @@ func TestCmdLinkGenerate_WaitUntil(t *testing.T) {
 			generateCredential: true,
 			outputType:         "yaml",
 			timeout:            time.Second,
-			tlsSecret:          "link-test",
+			tlsCredentials:     "link-test",
 			k8sObjects: []runtime.Object{
 				&corev1.Secret{
 					TypeMeta: metav1.TypeMeta{
@@ -876,7 +876,7 @@ func TestCmdLinkGenerate_WaitUntil(t *testing.T) {
 			generateCredential: true,
 			outputType:         "yaml",
 			timeout:            time.Second,
-			tlsSecret:          "link-test",
+			tlsCredentials:     "link-test",
 			k8sObjects: []runtime.Object{
 				&corev1.Secret{
 					TypeMeta: metav1.TypeMeta{
@@ -907,7 +907,7 @@ func TestCmdLinkGenerate_WaitUntil(t *testing.T) {
 
 			command.output = test.outputType
 			command.generateCredential = test.generateCredential
-			command.tlsSecret = test.tlsSecret
+			command.tlsCredentials = test.tlsCredentials
 			command.timeout = test.timeout
 
 			err := command.WaitUntil()
diff --git a/internal/cmd/skupper/link/kube/link_update_test.go b/internal/cmd/skupper/link/kube/link_update_test.go
index c64942a8..c5f83361 100644
--- a/internal/cmd/skupper/link/kube/link_update_test.go
+++ b/internal/cmd/skupper/link/kube/link_update_test.go
@@ -207,7 +207,7 @@ func TestCmdLinkUpdate_ValidateInput(t *testing.T) {
 		{
 			name:  "tls secret not available",
 			args:  []string{"my-link"},
-			flags: common.CommandLinkUpdateFlags{Cost: "1", TlsSecret: "secret", Timeout: time.Minute},
+			flags: common.CommandLinkUpdateFlags{Cost: "1", TlsCredentials: "secret", Timeout: time.Minute},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.SiteList{
 					Items: []v2alpha1.Site{
@@ -233,7 +233,7 @@ func TestCmdLinkUpdate_ValidateInput(t *testing.T) {
 		{
 			name:  "Timeout value is 0",
 			args:  []string{"my-link"},
-			flags: common.CommandLinkUpdateFlags{Cost: "1", TlsSecret: "secret", Timeout: time.Second * 0},
+			flags: common.CommandLinkUpdateFlags{Cost: "1", TlsCredentials: "secret", Timeout: time.Second * 0},
 			k8sObjects: []runtime.Object{
 				&v12.Secret{
 					ObjectMeta: v1.ObjectMeta{
@@ -287,24 +287,24 @@ func TestCmdLinkUpdate_ValidateInput(t *testing.T) {
 func TestCmdLinkUpdate_InputToOptions(t *testing.T) {
 
 	type test struct {
-		name              string
-		args              []string
-		flags             common.CommandLinkUpdateFlags
-		expectedTlsSecret string
-		expectedCost      int
-		expectedOutput    string
-		expectedTimeout   time.Duration
+		name                   string
+		args                   []string
+		flags                  common.CommandLinkUpdateFlags
+		expectedTlsCredentials string
+		expectedCost           int
+		expectedOutput         string
+		expectedTimeout        time.Duration
 	}
 
 	testTable := []test{
 		{
-			name:              "check options",
-			args:              []string{"my-link"},
-			flags:             common.CommandLinkUpdateFlags{"secret", "1", "json", time.Minute},
-			expectedCost:      1,
-			expectedTlsSecret: "secret",
-			expectedOutput:    "json",
-			expectedTimeout:   time.Minute,
+			name:                   "check options",
+			args:                   []string{"my-link"},
+			flags:                  common.CommandLinkUpdateFlags{"secret", "1", "json", time.Minute},
+			expectedCost:           1,
+			expectedTlsCredentials: "secret",
+			expectedOutput:         "json",
+			expectedTimeout:        time.Minute,
 		},
 	}
 
@@ -318,7 +318,7 @@ func TestCmdLinkUpdate_InputToOptions(t *testing.T) {
 			cmd.InputToOptions()
 
 			assert.Check(t, cmd.output == test.expectedOutput)
-			assert.Check(t, cmd.tlsSecret == test.expectedTlsSecret)
+			assert.Check(t, cmd.tlsCredentials == test.expectedTlsCredentials)
 			assert.Check(t, cmd.cost == test.expectedCost)
 			assert.Check(t, cmd.timeout == test.expectedTimeout)
 		})
@@ -333,17 +333,17 @@ func TestCmdLinkUpdate_Run(t *testing.T) {
 		linkName            string
 		Cost                int
 		output              string
-		tlsSecret           string
+		tlsCredentials      string
 		errorMessage        string
 		skupperErrorMessage string
 	}
 
 	testTable := []test{
 		{
-			name:      "runs ok",
-			linkName:  "my-link",
-			Cost:      1,
-			tlsSecret: "secret",
+			name:           "runs ok",
+			linkName:       "my-link",
+			Cost:           1,
+			tlsCredentials: "secret",
 			skupperObjects: []runtime.Object{
 				&v2alpha1.Link{
 					ObjectMeta: v1.ObjectMeta{
@@ -399,7 +399,7 @@ func TestCmdLinkUpdate_Run(t *testing.T) {
 
 		cmd.linkName = test.linkName
 		cmd.output = test.output
-		cmd.tlsSecret = test.tlsSecret
+		cmd.tlsCredentials = test.tlsCredentials
 		cmd.cost = test.Cost
 
 		t.Run(test.name, func(t *testing.T) {
diff --git a/internal/cmd/skupper/link/link_test.go b/internal/cmd/skupper/link/link_test.go
index 9ddc563c..6d0f21e0 100644
--- a/internal/cmd/skupper/link/link_test.go
+++ b/internal/cmd/skupper/link/link_test.go
@@ -22,7 +22,7 @@ func TestCmdLinkFactory(t *testing.T) {
 		{
 			name: "CmdLinkGenerateFactory",
 			expectedFlagsWithDefaultValue: map[string]interface{}{
-				common.FlagNameTlsSecret:          "",
+				common.FlagNameTlsCredentials:     "",
 				common.FlagNameCost:               "1",
 				common.FlagNameOutput:             "yaml",
 				common.FlagNameGenerateCredential: "true",
@@ -33,10 +33,10 @@ func TestCmdLinkFactory(t *testing.T) {
 		{
 			name: "CmdLinkUpdateFactory",
 			expectedFlagsWithDefaultValue: map[string]interface{}{
-				common.FlagNameTlsSecret: "",
-				common.FlagNameCost:      "1",
-				common.FlagNameOutput:    "",
-				common.FlagNameTimeout:   "1m0s",
+				common.FlagNameTlsCredentials: "",
+				common.FlagNameCost:           "1",
+				common.FlagNameOutput:         "",
+				common.FlagNameTimeout:        "1m0s",
 			},
 			command: CmdLinkUpdateFactory(types.PlatformKubernetes),
 		},
diff --git a/internal/cmd/skupper/listener/kube/listener_create_test.go b/internal/cmd/skupper/listener/kube/listener_create_test.go
index fd0105f0..ed58e98c 100644
--- a/internal/cmd/skupper/listener/kube/listener_create_test.go
+++ b/internal/cmd/skupper/listener/kube/listener_create_test.go
@@ -133,8 +133,8 @@ func TestCmdListenerCreate_ValidateInput(t *testing.T) {
 			name: "tls-secret does not exist",
 			args: []string{"my-listener-tls", "8080"},
 			flags: common.CommandListenerCreateFlags{
-				Timeout:   1 * time.Minute,
-				TlsSecret: "not-valid",
+				Timeout:        1 * time.Minute,
+				TlsCredentials: "not-valid",
 			},
 			expectedErrors: []string{"tls-secret is not valid: does not exist"},
 		},
@@ -158,12 +158,12 @@ func TestCmdListenerCreate_ValidateInput(t *testing.T) {
 			name: "flags all valid",
 			args: []string{"my-listener-flags", "8080"},
 			flags: common.CommandListenerCreateFlags{
-				Host:         "hostname",
-				RoutingKey:   "routingkeyname",
-				TlsSecret:    "secretname",
-				ListenerType: "tcp",
-				Timeout:      1 * time.Minute,
-				Output:       "json",
+				Host:           "hostname",
+				RoutingKey:     "routingkeyname",
+				TlsCredentials: "secretname",
+				ListenerType:   "tcp",
+				Timeout:        1 * time.Minute,
+				Output:         "json",
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.Listener{
@@ -216,37 +216,37 @@ func TestCmdListenerCreate_ValidateInput(t *testing.T) {
 func TestCmdListenerCreate_InputToOptions(t *testing.T) {
 
 	type test struct {
-		name                 string
-		flags                common.CommandListenerCreateFlags
-		Listenername         string
-		expectedTlsSecret    string
-		expectedHost         string
-		expectedRoutingKey   string
-		expectedListenerType string
-		expectedOutput       string
-		expectedTimeout      time.Duration
+		name                   string
+		flags                  common.CommandListenerCreateFlags
+		Listenername           string
+		expectedTlsCredentials string
+		expectedHost           string
+		expectedRoutingKey     string
+		expectedListenerType   string
+		expectedOutput         string
+		expectedTimeout        time.Duration
 	}
 
 	testTable := []test{
 		{
-			name:                 "test1",
-			flags:                common.CommandListenerCreateFlags{"backend", "backend", "secret", "tcp", 20 * time.Second, "json"},
-			expectedTlsSecret:    "secret",
-			expectedHost:         "backend",
-			expectedRoutingKey:   "backend",
-			expectedTimeout:      20 * time.Second,
-			expectedListenerType: "tcp",
-			expectedOutput:       "json",
+			name:                   "test1",
+			flags:                  common.CommandListenerCreateFlags{"backend", "backend", "secret", "tcp", 20 * time.Second, "json"},
+			expectedTlsCredentials: "secret",
+			expectedHost:           "backend",
+			expectedRoutingKey:     "backend",
+			expectedTimeout:        20 * time.Second,
+			expectedListenerType:   "tcp",
+			expectedOutput:         "json",
 		},
 		{
-			name:                 "test2",
-			flags:                common.CommandListenerCreateFlags{"", "", "secret", "tcp", 30 * time.Second, "yaml"},
-			expectedTlsSecret:    "secret",
-			expectedHost:         "test2",
-			expectedRoutingKey:   "test2",
-			expectedTimeout:      30 * time.Second,
-			expectedListenerType: "tcp",
-			expectedOutput:       "yaml",
+			name:                   "test2",
+			flags:                  common.CommandListenerCreateFlags{"", "", "secret", "tcp", 30 * time.Second, "yaml"},
+			expectedTlsCredentials: "secret",
+			expectedHost:           "test2",
+			expectedRoutingKey:     "test2",
+			expectedTimeout:        30 * time.Second,
+			expectedListenerType:   "tcp",
+			expectedOutput:         "yaml",
 		},
 	}
 
@@ -263,7 +263,7 @@ func TestCmdListenerCreate_InputToOptions(t *testing.T) {
 
 			assert.Check(t, cmd.routingKey == test.expectedRoutingKey)
 			assert.Check(t, cmd.output == test.expectedOutput)
-			assert.Check(t, cmd.tlsSecret == test.expectedTlsSecret)
+			assert.Check(t, cmd.tlsCredentials == test.expectedTlsCredentials)
 			assert.Check(t, cmd.host == test.expectedHost)
 			assert.Check(t, cmd.timeout == test.expectedTimeout)
 			assert.Check(t, cmd.output == test.expectedOutput)
@@ -290,10 +290,10 @@ func TestCmdListenerCreate_Run(t *testing.T) {
 			listenerName: "run-listener",
 			listenerPort: 8080,
 			flags: common.CommandListenerCreateFlags{
-				ListenerType: "tcp",
-				Host:         "hostname",
-				RoutingKey:   "keyname",
-				TlsSecret:    "secretname",
+				ListenerType:   "tcp",
+				Host:           "hostname",
+				RoutingKey:     "keyname",
+				TlsCredentials: "secretname",
 			},
 		},
 		{
@@ -301,11 +301,11 @@ func TestCmdListenerCreate_Run(t *testing.T) {
 			listenerName: "run-listener",
 			listenerPort: 8080,
 			flags: common.CommandListenerCreateFlags{
-				ListenerType: "tcp",
-				Host:         "hostname",
-				RoutingKey:   "keyname",
-				TlsSecret:    "secretname",
-				Output:       "yaml",
+				ListenerType:   "tcp",
+				Host:           "hostname",
+				RoutingKey:     "keyname",
+				TlsCredentials: "secretname",
+				Output:         "yaml",
 			},
 		},
 	}
diff --git a/internal/cmd/skupper/listener/kube/listener_update_test.go b/internal/cmd/skupper/listener/kube/listener_update_test.go
index fe3c6cd0..5194ac1e 100644
--- a/internal/cmd/skupper/listener/kube/listener_update_test.go
+++ b/internal/cmd/skupper/listener/kube/listener_update_test.go
@@ -115,8 +115,8 @@ func TestCmdListenerUpdate_ValidateInput(t *testing.T) {
 			name: "tls-secret is not valid",
 			args: []string{"my-listener-tls"},
 			flags: common.CommandListenerUpdateFlags{
-				TlsSecret: ":not-valid",
-				Timeout:   50 * time.Minute,
+				TlsCredentials: ":not-valid",
+				Timeout:        50 * time.Minute,
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.Listener{
@@ -221,13 +221,13 @@ func TestCmdListenerUpdate_ValidateInput(t *testing.T) {
 			name: "flags all valid",
 			args: []string{"my-listener-flags"},
 			flags: common.CommandListenerUpdateFlags{
-				Host:         "hostname",
-				RoutingKey:   "routingkeyname",
-				TlsSecret:    "secretname",
-				Port:         1234,
-				ListenerType: "tcp",
-				Timeout:      10 * time.Second,
-				Output:       "json",
+				Host:           "hostname",
+				RoutingKey:     "routingkeyname",
+				TlsCredentials: "secretname",
+				Port:           1234,
+				ListenerType:   "tcp",
+				Timeout:        10 * time.Second,
+				Output:         "json",
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.Listener{
@@ -294,12 +294,12 @@ func TestCmdListenerUpdate_Run(t *testing.T) {
 			name:         "runs ok",
 			listenerName: "run-listener",
 			flags: common.CommandListenerUpdateFlags{
-				ListenerType: "tcp",
-				Host:         "hostname",
-				RoutingKey:   "keyname",
-				TlsSecret:    "secretname",
-				Output:       "yaml",
-				Timeout:      1 * time.Minute,
+				ListenerType:   "tcp",
+				Host:           "hostname",
+				RoutingKey:     "keyname",
+				TlsCredentials: "secretname",
+				Output:         "yaml",
+				Timeout:        1 * time.Minute,
 			},
 			skupperObjects: []runtime.Object{
 				&v2alpha1.Listener{
diff --git a/internal/cmd/skupper/listener/listener_test.go b/internal/cmd/skupper/listener/listener_test.go
index 1160b2ed..eb7e3417 100644
--- a/internal/cmd/skupper/listener/listener_test.go
+++ b/internal/cmd/skupper/listener/listener_test.go
@@ -22,25 +22,25 @@ func TestCmdLinkFactory(t *testing.T) {
 		{
 			name: "CmdListenerCreateFactory",
 			expectedFlagsWithDefaultValue: map[string]interface{}{
-				common.FlagNameRoutingKey:   "",
-				common.FlagNameListenerHost: "",
-				common.FlagNameTlsSecret:    "",
-				common.FlagNameListenerType: "tcp",
-				common.FlagNameOutput:       "",
-				common.FlagNameTimeout:      "1m0s",
+				common.FlagNameRoutingKey:     "",
+				common.FlagNameListenerHost:   "",
+				common.FlagNameTlsCredentials: "",
+				common.FlagNameListenerType:   "tcp",
+				common.FlagNameOutput:         "",
+				common.FlagNameTimeout:        "1m0s",
 			},
 			command: CmdListenerCreateFactory(types.PlatformKubernetes),
 		},
 		{
 			name: "CmdListenerUpdateFactory",
 			expectedFlagsWithDefaultValue: map[string]interface{}{
-				common.FlagNameRoutingKey:   "",
-				common.FlagNameListenerHost: "",
-				common.FlagNameTlsSecret:    "",
-				common.FlagNameListenerType: "tcp",
-				common.FlagNameOutput:       "",
-				common.FlagNameTimeout:      "1m0s",
-				common.FlagNameListenerPort: "0",
+				common.FlagNameRoutingKey:     "",
+				common.FlagNameListenerHost:   "",
+				common.FlagNameTlsCredentials: "",
+				common.FlagNameListenerType:   "tcp",
+				common.FlagNameOutput:         "",
+				common.FlagNameTimeout:        "1m0s",
+				common.FlagNameListenerPort:   "0",
 			},
 			command: CmdListenerUpdateFactory(types.PlatformKubernetes),
 		},
diff --git a/pkg/kube/site/bindings_test.go b/pkg/kube/site/bindings_test.go
index b54d0267..67c16061 100644
--- a/pkg/kube/site/bindings_test.go
+++ b/pkg/kube/site/bindings_test.go
@@ -5,18 +5,20 @@ import (
 
 	skupperv2alpha1 "github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
 	"github.com/skupperproject/skupper/pkg/qdr"
-	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-
 	"gotest.tools/assert"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
 type MockBindingContext struct {
-	selectors map[string]TargetSelection
+	selectors     map[string]TargetSelection
+	exposed       ExposedPorts
+	unexposedHost string
 }
 
 func NewMockBindingContext(selectors map[string]TargetSelection) *MockBindingContext {
 	return &MockBindingContext{
 		selectors: selectors,
+		exposed:   map[string]*ExposedPortSet{},
 	}
 }
 
@@ -28,9 +30,12 @@ func (m *MockBindingContext) Select(connector *skupperv2alpha1.Connector) Target
 }
 
 func (m *MockBindingContext) Expose(ports *ExposedPortSet) {
+	portsCopy := *ports
+	m.exposed[ports.Host] = &portsCopy
 }
 
 func (m *MockBindingContext) Unexpose(host string) {
+	m.unexposedHost = host
 }
 
 type MockTargetSelection struct {
@@ -396,3 +401,218 @@ func TestBindingAdaptor_updateBridgeConfigForConnector(t *testing.T) {
 		})
 	}
 }
+
+func TestBindingAdaptor_ListenerUpdated(t *testing.T) {
+	type fields struct {
+		context   BindingContext
+		mapping   *qdr.PortMapping
+		exposed   ExposedPorts
+		selectors map[string]TargetSelection
+	}
+	type args struct {
+		listener *skupperv2alpha1.Listener
+	}
+	type expected struct {
+		exposed ExposedPorts
+	}
+	tests := []struct {
+		name     string
+		fields   fields
+		args     args
+		expected expected
+	}{
+		{
+			name: "Successfully expose listener",
+			fields: fields{
+				context: NewMockBindingContext(nil),
+				// TMPDBG mapping: &qdr.PortMapping{},
+				mapping:   qdr.RecoverPortMapping(&qdr.RouterConfig{}),
+				exposed:   ExposedPorts{},
+				selectors: map[string]TargetSelection{},
+			},
+			args: args{
+				listener: &skupperv2alpha1.Listener{
+					ObjectMeta: v1.ObjectMeta{
+						Name:      "backend",
+						Namespace: "test",
+						UID:       "8a96ffdf-403b-4e4a-83a8-97d3d459adb6",
+					},
+					Spec: skupperv2alpha1.ListenerSpec{
+						Host:       "backend",
+						Port:       8080,
+						RoutingKey: "backend",
+					},
+				},
+			},
+			expected: expected{
+				exposed: map[string]*ExposedPortSet{
+					"backend": &ExposedPortSet{
+						Host: "backend",
+						Ports: map[string]Port{
+							"backend": Port{
+								Name:       "backend",
+								Port:       8080,
+								TargetPort: 1024,
+								Protocol:   "TCP",
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			a := &BindingAdaptor{
+				context:   tt.fields.context,
+				mapping:   tt.fields.mapping,
+				exposed:   tt.fields.exposed,
+				selectors: tt.fields.selectors,
+			}
+			a.ListenerUpdated(tt.args.listener)
+
+			assert.DeepEqual(t, a.exposed, tt.expected.exposed)
+		})
+	}
+}
+
+func TestBindingAdaptor_ListenerDeleted(t *testing.T) {
+	type fields struct {
+		context   BindingContext
+		mapping   *qdr.PortMapping
+		exposed   ExposedPorts
+		selectors map[string]TargetSelection
+	}
+	type args struct {
+		listener *skupperv2alpha1.Listener
+	}
+	type expected struct {
+		unexposedHost string
+	}
+	tests := []struct {
+		name     string
+		fields   fields
+		args     args
+		expected expected
+	}{
+		{
+			name: "Successfully delete listener",
+			fields: fields{
+				context:   NewMockBindingContext(nil),
+				mapping:   qdr.RecoverPortMapping(&qdr.RouterConfig{}),
+				exposed:   ExposedPorts{},
+				selectors: map[string]TargetSelection{},
+			},
+			args: args{
+				listener: &skupperv2alpha1.Listener{
+					ObjectMeta: v1.ObjectMeta{
+						Name:      "backend",
+						Namespace: "test",
+						UID:       "8a96ffdf-403b-4e4a-83a8-97d3d459adb6",
+					},
+					Spec: skupperv2alpha1.ListenerSpec{
+						Host:       "backend",
+						Port:       8080,
+						RoutingKey: "backend",
+					},
+				},
+			},
+			expected: expected{
+				unexposedHost: "backend",
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			a := &BindingAdaptor{
+				context:   tt.fields.context,
+				mapping:   tt.fields.mapping,
+				exposed:   tt.fields.exposed,
+				selectors: tt.fields.selectors,
+			}
+			a.ListenerDeleted(tt.args.listener)
+
+			mockBindingContext, ok := a.context.(*MockBindingContext)
+			assert.Equal(t, ok, true)
+			assert.Equal(t, mockBindingContext.unexposedHost, tt.expected.unexposedHost)
+		})
+	}
+}
+
+func TestBindingAdaptor_updateBridgeConfigForListener(t *testing.T) {
+	type fields struct {
+		context   BindingContext
+		mapping   *qdr.PortMapping
+		exposed   ExposedPorts
+		selectors map[string]TargetSelection
+	}
+	type args struct {
+		siteId   string
+		listener *skupperv2alpha1.Listener
+		config   qdr.BridgeConfig
+	}
+	type expected struct {
+		config qdr.BridgeConfig
+	}
+	tests := []struct {
+		name     string
+		fields   fields
+		args     args
+		expected expected
+	}{
+		{
+			name: "listener updated successfully",
+			fields: fields{
+				context:   NewMockBindingContext(map[string]TargetSelection{}),
+				mapping:   qdr.RecoverPortMapping(&qdr.RouterConfig{}),
+				selectors: map[string]TargetSelection{},
+			},
+			args: args{
+				siteId: "00000000-0000-0000-0000-000000000001",
+				listener: &skupperv2alpha1.Listener{
+					ObjectMeta: v1.ObjectMeta{
+						Name:      "backend",
+						Namespace: "test",
+						UID:       "8a96ffdf-403b-4e4a-83a8-97d3d459adb6",
+					},
+					Spec: skupperv2alpha1.ListenerSpec{
+						Host:       "backend",
+						Port:       8080,
+						RoutingKey: "backend",
+					},
+				},
+				config: qdr.NewBridgeConfig(),
+			},
+			expected: expected{
+				config: qdr.BridgeConfig{
+					TcpListeners: map[string]qdr.TcpEndpoint{
+						"backend": qdr.TcpEndpoint{
+
+							Name:    "backend",
+							Host:    "0.0.0.0",
+							Port:    "1024",
+							Address: "backend",
+							SiteId:  "00000000-0000-0000-0000-000000000001",
+						},
+					},
+					TcpConnectors: map[string]qdr.TcpEndpoint{},
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			a := &BindingAdaptor{
+				context:   tt.fields.context,
+				mapping:   tt.fields.mapping,
+				exposed:   tt.fields.exposed,
+				selectors: tt.fields.selectors,
+			}
+			a.updateBridgeConfigForListener(tt.args.siteId, tt.args.listener, &tt.args.config)
+
+			assert.DeepEqual(t, tt.args.config, tt.expected.config)
+		})
+	}
+}