Repository: skupperproject/skupper
Pull request #1894 (2025-01-17 19:58:43)

Add Site Version to nonkube router configuration
Adds a version field to the skupper router's site configuration.

c-kruse: depends on https://github.com/skupperproject/skupper/pull/1893

Patch:
diff --git a/Makefile b/Makefile
index bc07e71d..8fac87b1 100644
--- a/Makefile
+++ b/Makefile
@@ -107,6 +107,7 @@ test:
 cover:
 	go test ${TESTFLAGS} \
 		-cover \
+		-coverpkg=./... \
 		-coverprofile cover.out \
 		./...
 
diff --git a/api/types/types.go b/api/types/types.go
index e326aa66..afc422e4 100644
--- a/api/types/types.go
+++ b/api/types/types.go
@@ -322,7 +322,7 @@ const (
 	PlatformKubernetes Platform = "kubernetes"
 	PlatformPodman     Platform = "podman"
 	PlatformDocker     Platform = "docker"
-	PlatformSystemd    Platform = "systemd"
+	PlatformLinux      Platform = "linux"
 )
 
 func (p Platform) IsKubernetes() bool {
diff --git a/cmd/bootstrap/README.md b/cmd/bootstrap/README.md
index 1d181ef5..de856334 100644
--- a/cmd/bootstrap/README.md
+++ b/cmd/bootstrap/README.md
@@ -31,7 +31,7 @@ allowed values:
 
 * podman
 * docker
-* systemd
+* linux
 
 ### Container engine platforms
 
@@ -39,9 +39,9 @@ When `podman` or `docker` platform is used, then the bootstrap procedure will
 require that the respective container engine endpoint is available. The default
 unix socket will be used based on the current user and platform selected.
 
-### Systemd
+### Linux
 
-The `systemd` platform actually requires that you have a local installation of
+The `linux` platform actually requires that you have a local installation of
 the `skupper-router` (`skrouterd` binary must be available in your PATH).
 
 ## Bootstrap usage
@@ -170,7 +170,7 @@ The bundle installation script accepts the following flags:
 
 ```
 -h               help
--p <platform>    podman, docker, systemd
+-p <platform>    podman, docker, linux
 -n <namespace>   if not provided, the namespace defined in the bundle is used (if none, default is used)
 -x               remove site and namespace
 -d <directory>   dump static links into the provided directory 
diff --git a/cmd/bootstrap/bootstrap.sh b/cmd/bootstrap/bootstrap.sh
index 6a0e3616..46f7e63d 100755
--- a/cmd/bootstrap/bootstrap.sh
+++ b/cmd/bootstrap/bootstrap.sh
@@ -51,9 +51,9 @@ container_env() {
     export CONTAINER_ENGINE="${CONTAINER_ENGINE:-podman}"
     # Determining if the respective binaries are available
     case "${SKUPPER_PLATFORM}" in
-        systemd)
-            command -v skrouterd > /dev/null || exit_error "SystemD platform cannot be used: skrouterd not found"
-            command -v "${CONTAINER_ENGINE}" > /dev/null || exit_error "SystemD platform cannot be used: ${CONTAINER_ENGINE} (container engine) not found"
+        linux)
+            command -v skrouterd > /dev/null || exit_error "Linux platform cannot be used: skrouterd not found"
+            command -v "${CONTAINER_ENGINE}" > /dev/null || exit_error "Linux platform cannot be used: ${CONTAINER_ENGINE} (container engine) not found"
             ;;
         docker)
             command -v docker > /dev/null || exit_error "Docker platform cannot be used: docker not found"
diff --git a/cmd/controller/main.go b/cmd/controller/main.go
index 4d0134d3..cfa49fa7 100644
--- a/cmd/controller/main.go
+++ b/cmd/controller/main.go
@@ -10,11 +10,8 @@ import (
 
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
-	iflag "github.com/skupperproject/skupper/internal/flag"
 	internalclient "github.com/skupperproject/skupper/internal/kube/client"
 	"github.com/skupperproject/skupper/internal/kube/controller"
-	"github.com/skupperproject/skupper/internal/kube/grants"
-	"github.com/skupperproject/skupper/internal/kube/securedaccess"
 	"github.com/skupperproject/skupper/pkg/version"
 )
 
@@ -44,28 +41,12 @@ func SetupSignalHandler() (stopCh <-chan struct{}) {
 
 func main() {
 	flags := flag.NewFlagSet("", flag.ExitOnError)
-	grantConfig, err := grants.BoundGrantConfig(flags)
+	config, err := controller.BoundConfig(flags)
 	if err != nil {
 		fmt.Println(err.Error())
 		os.Exit(1)
 	}
-	securedAccessConfig, err := securedaccess.BoundConfig(flags)
-	if err != nil {
-		fmt.Println(err.Error())
-		os.Exit(1)
-	}
-	if err := securedAccessConfig.Verify(); err != nil {
-		fmt.Println(err.Error())
-		os.Exit(1)
-	}
-
-	var namespace string
-	var kubeconfig string
-	iflag.StringVar(flags, &namespace, "namespace", "NAMESPACE", "", "The Kubernetes namespace scope for the controller")
-	iflag.StringVar(flags, &kubeconfig, "kubeconfig", "KUBECONFIG", "", "A path to the kubeconfig file to use")
 
-	var watchNamespace string
-	iflag.StringVar(flags, &watchNamespace, "watch-namespace", "WATCH_NAMESPACE", metav1.NamespaceAll, "The Kubernetes namespace the controller should monitor for controlled resources (will monitor all if not specified)")
 	// if -version used, report and exit
 	isVersion := flags.Bool("version", false, "Report the version of the Skupper Controller")
 	flags.Parse(os.Args[1:])
@@ -74,21 +55,21 @@ func main() {
 		os.Exit(0)
 	}
 	log.Printf("Version: %s", version.Version)
-	if watchNamespace == metav1.NamespaceAll {
+	if config.WatchNamespace == metav1.NamespaceAll {
 		log.Println("Skupper controller watching all namespaces")
 	} else {
-		log.Println("Skupper controller watching namespace", watchNamespace)
+		log.Println("Skupper controller watching namespace", config.WatchNamespace)
 	}
 
 	// set up signals so we handle the first shutdown signal gracefully
 	stopCh := SetupSignalHandler()
 
-	cli, err := internalclient.NewClient(namespace, "", kubeconfig)
+	cli, err := internalclient.NewClient(config.Namespace, "", config.Kubeconfig)
 	if err != nil {
 		log.Fatal("Error getting van client ", err.Error())
 	}
 
-	controller, err := controller.NewController(cli, grantConfig, securedAccessConfig, watchNamespace, cli.Namespace)
+	controller, err := controller.NewController(cli, config)
 	if err != nil {
 		log.Fatal("Error getting new site controller ", err.Error())
 	}
diff --git a/cmd/network-observer/internal/api/extras_gen.go b/cmd/network-observer/internal/api/extras_gen.go
index d42c0bf0..8c6bc291 100644
--- a/cmd/network-observer/internal/api/extras_gen.go
+++ b/cmd/network-observer/internal/api/extras_gen.go
@@ -4,26 +4,6 @@ package api
 // Implements ResponseSetter and CollectionResponseSetter for the generated
 // response objects
 
-// SetCount
-func (r *AddressListResponse) SetCount(v int64) {
-	r.Count = v
-}
-
-// SetResults
-func (r *AddressListResponse) SetResults(v []AddressRecord) {
-	r.Results = v
-}
-
-// SetTimeRangeCount
-func (r *AddressListResponse) SetTimeRangeCount(v int64) {
-	r.TimeRangeCount = v
-}
-
-// SetResults
-func (r *AddressResponse) SetResults(v AddressRecord) {
-	r.Results = v
-}
-
 // SetCount
 func (r *ApplicationFlowResponse) SetCount(v int64) {
 	r.Count = v
@@ -234,6 +214,26 @@ func (r *RouterResponse) SetResults(v RouterRecord) {
 	r.Results = v
 }
 
+// SetCount
+func (r *ServiceListResponse) SetCount(v int64) {
+	r.Count = v
+}
+
+// SetResults
+func (r *ServiceListResponse) SetResults(v []ServiceRecord) {
+	r.Results = v
+}
+
+// SetTimeRangeCount
+func (r *ServiceListResponse) SetTimeRangeCount(v int64) {
+	r.TimeRangeCount = v
+}
+
+// SetResults
+func (r *ServiceResponse) SetResults(v ServiceRecord) {
+	r.Results = v
+}
+
 // SetCount
 func (r *SiteListResponse) SetCount(v int64) {
 	r.Count = v
@@ -266,16 +266,6 @@ func (r *CollectionResponse) SetTimeRangeCount(v int64) {
 
 // Implements Record interface for the generated record objects
 
-// GetEndTime
-func (r AddressRecord) GetEndTime() uint64 {
-	return r.EndTime
-}
-
-// GetStartTime
-func (r AddressRecord) GetStartTime() uint64 {
-	return r.StartTime
-}
-
 // GetEndTime
 func (r ApplicationFlowRecord) GetEndTime() uint64 {
 	return r.EndTime
@@ -386,6 +376,16 @@ func (r RouterRecord) GetStartTime() uint64 {
 	return r.StartTime
 }
 
+// GetEndTime
+func (r ServiceRecord) GetEndTime() uint64 {
+	return r.EndTime
+}
+
+// GetStartTime
+func (r ServiceRecord) GetStartTime() uint64 {
+	return r.StartTime
+}
+
 // GetEndTime
 func (r SiteRecord) GetEndTime() uint64 {
 	return r.EndTime
diff --git a/cmd/network-observer/internal/api/types_gen.go b/cmd/network-observer/internal/api/types_gen.go
index 6c1fdad3..75c88460 100644
--- a/cmd/network-observer/internal/api/types_gen.go
+++ b/cmd/network-observer/internal/api/types_gen.go
@@ -54,51 +54,10 @@ const (
 	SitePlatformTypeDocker     SitePlatformType = "docker"
 	SitePlatformTypeKubernetes SitePlatformType = "kubernetes"
 	SitePlatformTypePodman     SitePlatformType = "podman"
-	SitePlatformTypeSystemd    SitePlatformType = "systemd"
+	SitePlatformTypeLinux      SitePlatformType = "linux"
 	SitePlatformTypeUnknown    SitePlatformType = "unknown"
 )
 
-// AddressListResponse defines model for AddressListResponse.
-type AddressListResponse struct {
-	// Count number of results in response
-	Count   int64           `json:"count"`
-	Results []AddressRecord `json:"results"`
-
-	// TimeRangeCount number of results matching filtering and time range constraints before any limit or offset is applied.
-	TimeRangeCount int64 `json:"timeRangeCount"`
-}
-
-// AddressRecord defines model for AddressRecord.
-type AddressRecord struct {
-	ConnectorCount int `json:"connectorCount"`
-
-	// EndTime The end time in microseconds of the record in Unix timestamp format.
-	EndTime uint64 `json:"endTime"`
-
-	// HasListener true when there is at least one listener for this address
-	HasListener bool `json:"hasListener"`
-
-	// Identity The unique identifier for the record.
-	Identity string `json:"identity"`
-
-	// IsBound true when there are both listeners and connectors configured
-	IsBound       bool   `json:"isBound"`
-	ListenerCount int    `json:"listenerCount"`
-	Name          string `json:"name"`
-
-	// ObservedApplicationProtocols Array of the observed application level protocols
-	ObservedApplicationProtocols []string `json:"observedApplicationProtocols"`
-	Protocol                     string   `json:"protocol"`
-
-	// StartTime The creation time in microseconds of the record in Unix timestamp format. The value 0 means that the record is not terminated
-	StartTime uint64 `json:"startTime"`
-}
-
-// AddressResponse defines model for AddressResponse.
-type AddressResponse struct {
-	Results AddressRecord `json:"results"`
-}
-
 // ApplicationFlowRecord defines model for ApplicationFlowRecord.
 type ApplicationFlowRecord struct {
 	ConnectionId    string  `json:"connectionId"`
@@ -213,22 +172,22 @@ type ConnectorListResponse struct {
 
 // ConnectorRecord defines model for ConnectorRecord.
 type ConnectorRecord struct {
-	Address   string  `json:"address"`
-	AddressId *string `json:"addressId,omitempty"`
-	DestHost  string  `json:"destHost"`
-	DestPort  string  `json:"destPort"`
+	DestHost string `json:"destHost"`
+	DestPort string `json:"destPort"`
 
 	// EndTime The end time in microseconds of the record in Unix timestamp format.
 	EndTime uint64 `json:"endTime"`
 
 	// Identity The unique identifier for the record.
-	Identity  string `json:"identity"`
-	Name      string `json:"name"`
-	Parent    string `json:"parent"`
-	ProcessId string `json:"processId"`
-	Protocol  string `json:"protocol"`
-	SiteId    string `json:"siteId"`
-	SiteName  string `json:"siteName"`
+	Identity   string  `json:"identity"`
+	Name       string  `json:"name"`
+	Parent     string  `json:"parent"`
+	ProcessId  string  `json:"processId"`
+	Protocol   string  `json:"protocol"`
+	RoutingKey string  `json:"routingKey"`
+	ServiceId  *string `json:"serviceId,omitempty"`
+	SiteId     string  `json:"siteId"`
+	SiteName   string  `json:"siteName"`
 
 	// StartTime The creation time in microseconds of the record in Unix timestamp format. The value 0 means that the record is not terminated
 	StartTime uint64  `json:"startTime"`
@@ -332,21 +291,21 @@ type ListenerListResponse struct {
 
 // ListenerRecord defines model for ListenerRecord.
 type ListenerRecord struct {
-	Address   string  `json:"address"`
-	AddressId *string `json:"addressId,omitempty"`
-	DestHost  string  `json:"destHost"`
-	DestPort  string  `json:"destPort"`
+	DestHost string `json:"destHost"`
+	DestPort string `json:"destPort"`
 
 	// EndTime The end time in microseconds of the record in Unix timestamp format.
 	EndTime uint64 `json:"endTime"`
 
 	// Identity The unique identifier for the record.
-	Identity string `json:"identity"`
-	Name     string `json:"name"`
-	Parent   string `json:"parent"`
-	Protocol string `json:"protocol"`
-	SiteId   string `json:"siteId"`
-	SiteName string `json:"siteName"`
+	Identity   string  `json:"identity"`
+	Name       string  `json:"name"`
+	Parent     string  `json:"parent"`
+	Protocol   string  `json:"protocol"`
+	RoutingKey string  `json:"routingKey"`
+	ServiceId  *string `json:"serviceId,omitempty"`
+	SiteId     string  `json:"siteId"`
+	SiteName   string  `json:"siteName"`
 
 	// StartTime The creation time in microseconds of the record in Unix timestamp format. The value 0 means that the record is not terminated
 	StartTime uint64 `json:"startTime"`
@@ -399,8 +358,6 @@ type ProcessListResponse struct {
 
 // ProcessRecord defines model for ProcessRecord.
 type ProcessRecord struct {
-	Addresses *[]AddressIdentifierType `json:"addresses"`
-
 	// EndTime The end time in microseconds of the record in Unix timestamp format.
 	EndTime uint64 `json:"endTime"`
 
@@ -425,6 +382,7 @@ type ProcessRecord struct {
 
 	// ProcessRole Internal processes are processes related to Skupper. Remote processes are processes indirectly connected, such as a proxy
 	ProcessRole ProcessRecordProcessRole `json:"processRole"`
+	Services    *[]ServiceIdentifierType `json:"services"`
 
 	// SourceHost The IP address of the node where the pod is running
 	SourceHost string `json:"sourceHost"`
@@ -564,6 +522,47 @@ type RouterResponse struct {
 	Results RouterRecord `json:"results"`
 }
 
+// ServiceListResponse defines model for ServiceListResponse.
+type ServiceListResponse struct {
+	// Count number of results in response
+	Count   int64           `json:"count"`
+	Results []ServiceRecord `json:"results"`
+
+	// TimeRangeCount number of results matching filtering and time range constraints before any limit or offset is applied.
+	TimeRangeCount int64 `json:"timeRangeCount"`
+}
+
+// ServiceRecord defines model for ServiceRecord.
+type ServiceRecord struct {
+	ConnectorCount int `json:"connectorCount"`
+
+	// EndTime The end time in microseconds of the record in Unix timestamp format.
+	EndTime uint64 `json:"endTime"`
+
+	// HasListener true when there is at least one listener for this routingKey
+	HasListener bool `json:"hasListener"`
+
+	// Identity The unique identifier for the record.
+	Identity string `json:"identity"`
+
+	// IsBound true when there are both listeners and connectors configured
+	IsBound       bool   `json:"isBound"`
+	ListenerCount int    `json:"listenerCount"`
+	Name          string `json:"name"`
+
+	// ObservedApplicationProtocols Array of the observed application level protocols
+	ObservedApplicationProtocols []string `json:"observedApplicationProtocols"`
+	Protocol                     string   `json:"protocol"`
+
+	// StartTime The creation time in microseconds of the record in Unix timestamp format. The value 0 means that the record is not terminated
+	StartTime uint64 `json:"startTime"`
+}
+
+// ServiceResponse defines model for ServiceResponse.
+type ServiceResponse struct {
+	Results ServiceRecord `json:"results"`
+}
+
 // SiteListResponse defines model for SiteListResponse.
 type SiteListResponse struct {
 	// Count number of results in response
@@ -603,9 +602,6 @@ type SiteResponse struct {
 	Results SiteRecord `json:"results"`
 }
 
-// AddressIdentifierType a special string for identifying addresses uses the form `name@identity@protocol`
-type AddressIdentifierType = AtmarkDelimitedString
-
 // BaseRecord defines model for baseRecord.
 type BaseRecord struct {
 	// EndTime The end time in microseconds of the record in Unix timestamp format.
@@ -639,6 +635,9 @@ type OperStatusType string
 // ProcessBindingType Indicates whether a process is exposed or not in a skupper network
 type ProcessBindingType string
 
+// ServiceIdentifierType a special string for identifying services uses the form `name@identity@protocol`
+type ServiceIdentifierType = AtmarkDelimitedString
+
 // SitePlatformType The platform used for the site.
 type SitePlatformType string
 
@@ -651,12 +650,6 @@ type ErrorBadRequest = ErrorResponse
 // ErrorNotFound defines model for errorNotFound.
 type ErrorNotFound = ErrorResponse
 
-// GetAddressByID defines model for getAddressByID.
-type GetAddressByID = AddressResponse
-
-// GetAddresses defines model for getAddresses.
-type GetAddresses = AddressListResponse
-
 // GetApplicationFlows defines model for getApplicationFlows.
 type GetApplicationFlows = ApplicationFlowResponse
 
@@ -717,6 +710,12 @@ type GetRouterLinks = RouterLinkListResponse
 // GetRouters defines model for getRouters.
 type GetRouters = RouterListResponse
 
+// GetServiceByID defines model for getServiceByID.
+type GetServiceByID = ServiceResponse
+
+// GetServices defines model for getServices.
+type GetServices = ServiceListResponse
+
 // GetSiteByID defines model for getSiteByID.
 type GetSiteByID = SiteResponse
 
@@ -799,21 +798,6 @@ func WithRequestEditorFn(fn RequestEditorFn) ClientOption {
 
 // The interface specification for the client above.
 type ClientInterface interface {
-	// Addresses request
-	Addresses(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// AddressByID request
-	AddressByID(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// ConnectionsByAddress request
-	ConnectionsByAddress(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// ProcessesByAddress request
-	ProcessesByAddress(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// ProcessPairsByAddress request
-	ProcessPairsByAddress(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*http.Response, error)
-
 	// Applicationflows request
 	Applicationflows(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
 
@@ -889,6 +873,21 @@ type ClientInterface interface {
 	// LinksByRouter request
 	LinksByRouter(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*http.Response, error)
 
+	// Services request
+	Services(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
+
+	// ServiceByID request
+	ServiceByID(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*http.Response, error)
+
+	// ConnectionsByService request
+	ConnectionsByService(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*http.Response, error)
+
+	// ProcessesByService request
+	ProcessesByService(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*http.Response, error)
+
+	// ProcessPairsByService request
+	ProcessPairsByService(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*http.Response, error)
+
 	// Sitepairs request
 	Sitepairs(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
 
@@ -914,66 +913,6 @@ type ClientInterface interface {
 	RoutersBySite(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*http.Response, error)
 }
 
-func (c *Client) Addresses(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewAddressesRequest(c.Server)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) AddressByID(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewAddressByIDRequest(c.Server, id)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) ConnectionsByAddress(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewConnectionsByAddressRequest(c.Server, id)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) ProcessesByAddress(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewProcessesByAddressRequest(c.Server, id)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) ProcessPairsByAddress(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewProcessPairsByAddressRequest(c.Server, id)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
 func (c *Client) Applicationflows(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
 	req, err := NewApplicationflowsRequest(c.Server)
 	if err != nil {
@@ -1274,6 +1213,66 @@ func (c *Client) LinksByRouter(ctx context.Context, id PathID, reqEditors ...Req
 	return c.Client.Do(req)
 }
 
+func (c *Client) Services(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
+	req, err := NewServicesRequest(c.Server)
+	if err != nil {
+		return nil, err
+	}
+	req = req.WithContext(ctx)
+	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
+		return nil, err
+	}
+	return c.Client.Do(req)
+}
+
+func (c *Client) ServiceByID(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*http.Response, error) {
+	req, err := NewServiceByIDRequest(c.Server, id)
+	if err != nil {
+		return nil, err
+	}
+	req = req.WithContext(ctx)
+	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
+		return nil, err
+	}
+	return c.Client.Do(req)
+}
+
+func (c *Client) ConnectionsByService(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*http.Response, error) {
+	req, err := NewConnectionsByServiceRequest(c.Server, id)
+	if err != nil {
+		return nil, err
+	}
+	req = req.WithContext(ctx)
+	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
+		return nil, err
+	}
+	return c.Client.Do(req)
+}
+
+func (c *Client) ProcessesByService(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*http.Response, error) {
+	req, err := NewProcessesByServiceRequest(c.Server, id)
+	if err != nil {
+		return nil, err
+	}
+	req = req.WithContext(ctx)
+	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
+		return nil, err
+	}
+	return c.Client.Do(req)
+}
+
+func (c *Client) ProcessPairsByService(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*http.Response, error) {
+	req, err := NewProcessPairsByServiceRequest(c.Server, id)
+	if err != nil {
+		return nil, err
+	}
+	req = req.WithContext(ctx)
+	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
+		return nil, err
+	}
+	return c.Client.Do(req)
+}
+
 func (c *Client) Sitepairs(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
 	req, err := NewSitepairsRequest(c.Server)
 	if err != nil {
@@ -1370,8 +1369,8 @@ func (c *Client) RoutersBySite(ctx context.Context, id PathID, reqEditors ...Req
 	return c.Client.Do(req)
 }
 
-// NewAddressesRequest generates requests for Addresses
-func NewAddressesRequest(server string) (*http.Request, error) {
+// NewApplicationflowsRequest generates requests for Applicationflows
+func NewApplicationflowsRequest(server string) (*http.Request, error) {
 	var err error
 
 	serverURL, err := url.Parse(server)
@@ -1379,7 +1378,7 @@ func NewAddressesRequest(server string) (*http.Request, error) {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/addresses")
+	operationPath := fmt.Sprintf("/api/v2alpha1/applicationflows")
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -1397,23 +1396,43 @@ func NewAddressesRequest(server string) (*http.Request, error) {
 	return req, nil
 }
 
-// NewAddressByIDRequest generates requests for AddressByID
-func NewAddressByIDRequest(server string, id PathID) (*http.Request, error) {
+// NewConnectionsRequest generates requests for Connections
+func NewConnectionsRequest(server string) (*http.Request, error) {
 	var err error
 
-	var pathParam0 string
+	serverURL, err := url.Parse(server)
+	if err != nil {
+		return nil, err
+	}
 
-	pathParam0, err = runtime.StyleParamWithLocation("simple", false, "id", runtime.ParamLocationPath, id)
+	operationPath := fmt.Sprintf("/api/v2alpha1/connections")
+	if operationPath[0] == '/' {
+		operationPath = "." + operationPath
+	}
+
+	queryURL, err := serverURL.Parse(operationPath)
+	if err != nil {
+		return nil, err
+	}
+
+	req, err := http.NewRequest("GET", queryURL.String(), nil)
 	if err != nil {
 		return nil, err
 	}
 
+	return req, nil
+}
+
+// NewConnectorsRequest generates requests for Connectors
+func NewConnectorsRequest(server string) (*http.Request, error) {
+	var err error
+
 	serverURL, err := url.Parse(server)
 	if err != nil {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/addresses/%s", pathParam0)
+	operationPath := fmt.Sprintf("/api/v2alpha1/connectors")
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -1431,8 +1450,8 @@ func NewAddressByIDRequest(server string, id PathID) (*http.Request, error) {
 	return req, nil
 }
 
-// NewConnectionsByAddressRequest generates requests for ConnectionsByAddress
-func NewConnectionsByAddressRequest(server string, id PathID) (*http.Request, error) {
+// NewConnectorByIDRequest generates requests for ConnectorByID
+func NewConnectorByIDRequest(server string, id PathID) (*http.Request, error) {
 	var err error
 
 	var pathParam0 string
@@ -1447,7 +1466,7 @@ func NewConnectionsByAddressRequest(server string, id PathID) (*http.Request, er
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/addresses/%s/connections", pathParam0)
+	operationPath := fmt.Sprintf("/api/v2alpha1/connectors/%s", pathParam0)
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -1465,23 +1484,16 @@ func NewConnectionsByAddressRequest(server string, id PathID) (*http.Request, er
 	return req, nil
 }
 
-// NewProcessesByAddressRequest generates requests for ProcessesByAddress
-func NewProcessesByAddressRequest(server string, id PathID) (*http.Request, error) {
+// NewHostsRequest generates requests for Hosts
+func NewHostsRequest(server string) (*http.Request, error) {
 	var err error
 
-	var pathParam0 string
-
-	pathParam0, err = runtime.StyleParamWithLocation("simple", false, "id", runtime.ParamLocationPath, id)
-	if err != nil {
-		return nil, err
-	}
-
 	serverURL, err := url.Parse(server)
 	if err != nil {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/addresses/%s/processes", pathParam0)
+	operationPath := fmt.Sprintf("/api/v2alpha1/hosts")
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -1499,8 +1511,8 @@ func NewProcessesByAddressRequest(server string, id PathID) (*http.Request, erro
 	return req, nil
 }
 
-// NewProcessPairsByAddressRequest generates requests for ProcessPairsByAddress
-func NewProcessPairsByAddressRequest(server string, id PathID) (*http.Request, error) {
+// NewHostsByIDRequest generates requests for HostsByID
+func NewHostsByIDRequest(server string, id PathID) (*http.Request, error) {
 	var err error
 
 	var pathParam0 string
@@ -1515,7 +1527,7 @@ func NewProcessPairsByAddressRequest(server string, id PathID) (*http.Request, e
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/addresses/%s/processpairs", pathParam0)
+	operationPath := fmt.Sprintf("/api/v2alpha1/hosts/%s", pathParam0)
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -1533,8 +1545,8 @@ func NewProcessPairsByAddressRequest(server string, id PathID) (*http.Request, e
 	return req, nil
 }
 
-// NewApplicationflowsRequest generates requests for Applicationflows
-func NewApplicationflowsRequest(server string) (*http.Request, error) {
+// NewLinksRequest generates requests for Links
+func NewLinksRequest(server string) (*http.Request, error) {
 	var err error
 
 	serverURL, err := url.Parse(server)
@@ -1542,7 +1554,7 @@ func NewApplicationflowsRequest(server string) (*http.Request, error) {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/applicationflows")
+	operationPath := fmt.Sprintf("/api/v2alpha1/links")
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -1560,16 +1572,23 @@ func NewApplicationflowsRequest(server string) (*http.Request, error) {
 	return req, nil
 }
 
-// NewConnectionsRequest generates requests for Connections
-func NewConnectionsRequest(server string) (*http.Request, error) {
+// NewLinkByIDRequest generates requests for LinkByID
+func NewLinkByIDRequest(server string, id PathID) (*http.Request, error) {
 	var err error
 
+	var pathParam0 string
+
+	pathParam0, err = runtime.StyleParamWithLocation("simple", false, "id", runtime.ParamLocationPath, id)
+	if err != nil {
+		return nil, err
+	}
+
 	serverURL, err := url.Parse(server)
 	if err != nil {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/connections")
+	operationPath := fmt.Sprintf("/api/v2alpha1/links/%s", pathParam0)
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -1587,8 +1606,8 @@ func NewConnectionsRequest(server string) (*http.Request, error) {
 	return req, nil
 }
 
-// NewConnectorsRequest generates requests for Connectors
-func NewConnectorsRequest(server string) (*http.Request, error) {
+// NewListenersRequest generates requests for Listeners
+func NewListenersRequest(server string) (*http.Request, error) {
 	var err error
 
 	serverURL, err := url.Parse(server)
@@ -1596,7 +1615,7 @@ func NewConnectorsRequest(server string) (*http.Request, error) {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/connectors")
+	operationPath := fmt.Sprintf("/api/v2alpha1/listeners")
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -1614,8 +1633,8 @@ func NewConnectorsRequest(server string) (*http.Request, error) {
 	return req, nil
 }
 
-// NewConnectorByIDRequest generates requests for ConnectorByID
-func NewConnectorByIDRequest(server string, id PathID) (*http.Request, error) {
+// NewListenerByIDRequest generates requests for ListenerByID
+func NewListenerByIDRequest(server string, id PathID) (*http.Request, error) {
 	var err error
 
 	var pathParam0 string
@@ -1630,7 +1649,7 @@ func NewConnectorByIDRequest(server string, id PathID) (*http.Request, error) {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/connectors/%s", pathParam0)
+	operationPath := fmt.Sprintf("/api/v2alpha1/listeners/%s", pathParam0)
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -1648,8 +1667,8 @@ func NewConnectorByIDRequest(server string, id PathID) (*http.Request, error) {
 	return req, nil
 }
 
-// NewHostsRequest generates requests for Hosts
-func NewHostsRequest(server string) (*http.Request, error) {
+// NewProcessesRequest generates requests for Processes
+func NewProcessesRequest(server string) (*http.Request, error) {
 	var err error
 
 	serverURL, err := url.Parse(server)
@@ -1657,7 +1676,7 @@ func NewHostsRequest(server string) (*http.Request, error) {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/hosts")
+	operationPath := fmt.Sprintf("/api/v2alpha1/processes")
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -1675,8 +1694,8 @@ func NewHostsRequest(server string) (*http.Request, error) {
 	return req, nil
 }
 
-// NewHostsByIDRequest generates requests for HostsByID
-func NewHostsByIDRequest(server string, id PathID) (*http.Request, error) {
+// NewProcessByIdRequest generates requests for ProcessById
+func NewProcessByIdRequest(server string, id PathID) (*http.Request, error) {
 	var err error
 
 	var pathParam0 string
@@ -1691,7 +1710,7 @@ func NewHostsByIDRequest(server string, id PathID) (*http.Request, error) {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/hosts/%s", pathParam0)
+	operationPath := fmt.Sprintf("/api/v2alpha1/processes/%s", pathParam0)
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -1709,8 +1728,8 @@ func NewHostsByIDRequest(server string, id PathID) (*http.Request, error) {
 	return req, nil
 }
 
-// NewLinksRequest generates requests for Links
-func NewLinksRequest(server string) (*http.Request, error) {
+// NewProcessgrouppairsRequest generates requests for Processgrouppairs
+func NewProcessgrouppairsRequest(server string) (*http.Request, error) {
 	var err error
 
 	serverURL, err := url.Parse(server)
@@ -1718,7 +1737,7 @@ func NewLinksRequest(server string) (*http.Request, error) {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/links")
+	operationPath := fmt.Sprintf("/api/v2alpha1/processgrouppairs")
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -1736,8 +1755,8 @@ func NewLinksRequest(server string) (*http.Request, error) {
 	return req, nil
 }
 
-// NewLinkByIDRequest generates requests for LinkByID
-func NewLinkByIDRequest(server string, id PathID) (*http.Request, error) {
+// NewProcessgrouppairByIDRequest generates requests for ProcessgrouppairByID
+func NewProcessgrouppairByIDRequest(server string, id PathID) (*http.Request, error) {
 	var err error
 
 	var pathParam0 string
@@ -1752,7 +1771,7 @@ func NewLinkByIDRequest(server string, id PathID) (*http.Request, error) {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/links/%s", pathParam0)
+	operationPath := fmt.Sprintf("/api/v2alpha1/processgrouppairs/%s", pathParam0)
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -1770,8 +1789,8 @@ func NewLinkByIDRequest(server string, id PathID) (*http.Request, error) {
 	return req, nil
 }
 
-// NewListenersRequest generates requests for Listeners
-func NewListenersRequest(server string) (*http.Request, error) {
+// NewProcessgroupsRequest generates requests for Processgroups
+func NewProcessgroupsRequest(server string) (*http.Request, error) {
 	var err error
 
 	serverURL, err := url.Parse(server)
@@ -1779,7 +1798,7 @@ func NewListenersRequest(server string) (*http.Request, error) {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/listeners")
+	operationPath := fmt.Sprintf("/api/v2alpha1/processgroups")
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -1797,8 +1816,8 @@ func NewListenersRequest(server string) (*http.Request, error) {
 	return req, nil
 }
 
-// NewListenerByIDRequest generates requests for ListenerByID
-func NewListenerByIDRequest(server string, id PathID) (*http.Request, error) {
+// NewProcessgroupByIDRequest generates requests for ProcessgroupByID
+func NewProcessgroupByIDRequest(server string, id PathID) (*http.Request, error) {
 	var err error
 
 	var pathParam0 string
@@ -1813,7 +1832,7 @@ func NewListenerByIDRequest(server string, id PathID) (*http.Request, error) {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/listeners/%s", pathParam0)
+	operationPath := fmt.Sprintf("/api/v2alpha1/processgroups/%s", pathParam0)
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -1831,8 +1850,8 @@ func NewListenerByIDRequest(server string, id PathID) (*http.Request, error) {
 	return req, nil
 }
 
-// NewProcessesRequest generates requests for Processes
-func NewProcessesRequest(server string) (*http.Request, error) {
+// NewProcesspairsRequest generates requests for Processpairs
+func NewProcesspairsRequest(server string) (*http.Request, error) {
 	var err error
 
 	serverURL, err := url.Parse(server)
@@ -1840,7 +1859,7 @@ func NewProcessesRequest(server string) (*http.Request, error) {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/processes")
+	operationPath := fmt.Sprintf("/api/v2alpha1/processpairs")
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -1858,8 +1877,8 @@ func NewProcessesRequest(server string) (*http.Request, error) {
 	return req, nil
 }
 
-// NewProcessByIdRequest generates requests for ProcessById
-func NewProcessByIdRequest(server string, id PathID) (*http.Request, error) {
+// NewProcesspairByIDRequest generates requests for ProcesspairByID
+func NewProcesspairByIDRequest(server string, id PathID) (*http.Request, error) {
 	var err error
 
 	var pathParam0 string
@@ -1874,7 +1893,7 @@ func NewProcessByIdRequest(server string, id PathID) (*http.Request, error) {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/processes/%s", pathParam0)
+	operationPath := fmt.Sprintf("/api/v2alpha1/processpairs/%s", pathParam0)
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -1892,8 +1911,8 @@ func NewProcessByIdRequest(server string, id PathID) (*http.Request, error) {
 	return req, nil
 }
 
-// NewProcessgrouppairsRequest generates requests for Processgrouppairs
-func NewProcessgrouppairsRequest(server string) (*http.Request, error) {
+// NewRouteraccessRequest generates requests for Routeraccess
+func NewRouteraccessRequest(server string) (*http.Request, error) {
 	var err error
 
 	serverURL, err := url.Parse(server)
@@ -1901,7 +1920,7 @@ func NewProcessgrouppairsRequest(server string) (*http.Request, error) {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/processgrouppairs")
+	operationPath := fmt.Sprintf("/api/v2alpha1/routeraccess")
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -1919,8 +1938,8 @@ func NewProcessgrouppairsRequest(server string) (*http.Request, error) {
 	return req, nil
 }
 
-// NewProcessgrouppairByIDRequest generates requests for ProcessgrouppairByID
-func NewProcessgrouppairByIDRequest(server string, id PathID) (*http.Request, error) {
+// NewRouteraccessByIDRequest generates requests for RouteraccessByID
+func NewRouteraccessByIDRequest(server string, id PathID) (*http.Request, error) {
 	var err error
 
 	var pathParam0 string
@@ -1935,7 +1954,7 @@ func NewProcessgrouppairByIDRequest(server string, id PathID) (*http.Request, er
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/processgrouppairs/%s", pathParam0)
+	operationPath := fmt.Sprintf("/api/v2alpha1/routeraccess/%s", pathParam0)
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -1953,8 +1972,8 @@ func NewProcessgrouppairByIDRequest(server string, id PathID) (*http.Request, er
 	return req, nil
 }
 
-// NewProcessgroupsRequest generates requests for Processgroups
-func NewProcessgroupsRequest(server string) (*http.Request, error) {
+// NewRouterlinksRequest generates requests for Routerlinks
+func NewRouterlinksRequest(server string) (*http.Request, error) {
 	var err error
 
 	serverURL, err := url.Parse(server)
@@ -1962,7 +1981,7 @@ func NewProcessgroupsRequest(server string) (*http.Request, error) {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/processgroups")
+	operationPath := fmt.Sprintf("/api/v2alpha1/routerlinks")
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -1980,8 +1999,8 @@ func NewProcessgroupsRequest(server string) (*http.Request, error) {
 	return req, nil
 }
 
-// NewProcessgroupByIDRequest generates requests for ProcessgroupByID
-func NewProcessgroupByIDRequest(server string, id PathID) (*http.Request, error) {
+// NewRouterlinkByIDRequest generates requests for RouterlinkByID
+func NewRouterlinkByIDRequest(server string, id PathID) (*http.Request, error) {
 	var err error
 
 	var pathParam0 string
@@ -1996,7 +2015,7 @@ func NewProcessgroupByIDRequest(server string, id PathID) (*http.Request, error)
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/processgroups/%s", pathParam0)
+	operationPath := fmt.Sprintf("/api/v2alpha1/routerlinks/%s", pathParam0)
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -2014,8 +2033,8 @@ func NewProcessgroupByIDRequest(server string, id PathID) (*http.Request, error)
 	return req, nil
 }
 
-// NewProcesspairsRequest generates requests for Processpairs
-func NewProcesspairsRequest(server string) (*http.Request, error) {
+// NewRoutersRequest generates requests for Routers
+func NewRoutersRequest(server string) (*http.Request, error) {
 	var err error
 
 	serverURL, err := url.Parse(server)
@@ -2023,7 +2042,7 @@ func NewProcesspairsRequest(server string) (*http.Request, error) {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/processpairs")
+	operationPath := fmt.Sprintf("/api/v2alpha1/routers")
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -2041,8 +2060,8 @@ func NewProcesspairsRequest(server string) (*http.Request, error) {
 	return req, nil
 }
 
-// NewProcesspairByIDRequest generates requests for ProcesspairByID
-func NewProcesspairByIDRequest(server string, id PathID) (*http.Request, error) {
+// NewRouterByIDRequest generates requests for RouterByID
+func NewRouterByIDRequest(server string, id PathID) (*http.Request, error) {
 	var err error
 
 	var pathParam0 string
@@ -2057,34 +2076,7 @@ func NewProcesspairByIDRequest(server string, id PathID) (*http.Request, error)
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/processpairs/%s", pathParam0)
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("GET", queryURL.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return req, nil
-}
-
-// NewRouteraccessRequest generates requests for Routeraccess
-func NewRouteraccessRequest(server string) (*http.Request, error) {
-	var err error
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/api/v2alpha1/routeraccess")
+	operationPath := fmt.Sprintf("/api/v2alpha1/routers/%s", pathParam0)
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -2102,8 +2094,8 @@ func NewRouteraccessRequest(server string) (*http.Request, error) {
 	return req, nil
 }
 
-// NewRouteraccessByIDRequest generates requests for RouteraccessByID
-func NewRouteraccessByIDRequest(server string, id PathID) (*http.Request, error) {
+// NewLinksByRouterRequest generates requests for LinksByRouter
+func NewLinksByRouterRequest(server string, id PathID) (*http.Request, error) {
 	var err error
 
 	var pathParam0 string
@@ -2118,7 +2110,7 @@ func NewRouteraccessByIDRequest(server string, id PathID) (*http.Request, error)
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/routeraccess/%s", pathParam0)
+	operationPath := fmt.Sprintf("/api/v2alpha1/routers/%s/links", pathParam0)
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -2136,8 +2128,8 @@ func NewRouteraccessByIDRequest(server string, id PathID) (*http.Request, error)
 	return req, nil
 }
 
-// NewRouterlinksRequest generates requests for Routerlinks
-func NewRouterlinksRequest(server string) (*http.Request, error) {
+// NewServicesRequest generates requests for Services
+func NewServicesRequest(server string) (*http.Request, error) {
 	var err error
 
 	serverURL, err := url.Parse(server)
@@ -2145,7 +2137,7 @@ func NewRouterlinksRequest(server string) (*http.Request, error) {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/routerlinks")
+	operationPath := fmt.Sprintf("/api/v2alpha1/services")
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -2163,8 +2155,8 @@ func NewRouterlinksRequest(server string) (*http.Request, error) {
 	return req, nil
 }
 
-// NewRouterlinkByIDRequest generates requests for RouterlinkByID
-func NewRouterlinkByIDRequest(server string, id PathID) (*http.Request, error) {
+// NewServiceByIDRequest generates requests for ServiceByID
+func NewServiceByIDRequest(server string, id PathID) (*http.Request, error) {
 	var err error
 
 	var pathParam0 string
@@ -2179,7 +2171,7 @@ func NewRouterlinkByIDRequest(server string, id PathID) (*http.Request, error) {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/routerlinks/%s", pathParam0)
+	operationPath := fmt.Sprintf("/api/v2alpha1/services/%s", pathParam0)
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -2197,16 +2189,23 @@ func NewRouterlinkByIDRequest(server string, id PathID) (*http.Request, error) {
 	return req, nil
 }
 
-// NewRoutersRequest generates requests for Routers
-func NewRoutersRequest(server string) (*http.Request, error) {
+// NewConnectionsByServiceRequest generates requests for ConnectionsByService
+func NewConnectionsByServiceRequest(server string, id PathID) (*http.Request, error) {
 	var err error
 
+	var pathParam0 string
+
+	pathParam0, err = runtime.StyleParamWithLocation("simple", false, "id", runtime.ParamLocationPath, id)
+	if err != nil {
+		return nil, err
+	}
+
 	serverURL, err := url.Parse(server)
 	if err != nil {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/routers")
+	operationPath := fmt.Sprintf("/api/v2alpha1/services/%s/connections", pathParam0)
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -2224,8 +2223,8 @@ func NewRoutersRequest(server string) (*http.Request, error) {
 	return req, nil
 }
 
-// NewRouterByIDRequest generates requests for RouterByID
-func NewRouterByIDRequest(server string, id PathID) (*http.Request, error) {
+// NewProcessesByServiceRequest generates requests for ProcessesByService
+func NewProcessesByServiceRequest(server string, id PathID) (*http.Request, error) {
 	var err error
 
 	var pathParam0 string
@@ -2240,7 +2239,7 @@ func NewRouterByIDRequest(server string, id PathID) (*http.Request, error) {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/routers/%s", pathParam0)
+	operationPath := fmt.Sprintf("/api/v2alpha1/services/%s/processes", pathParam0)
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -2258,8 +2257,8 @@ func NewRouterByIDRequest(server string, id PathID) (*http.Request, error) {
 	return req, nil
 }
 
-// NewLinksByRouterRequest generates requests for LinksByRouter
-func NewLinksByRouterRequest(server string, id PathID) (*http.Request, error) {
+// NewProcessPairsByServiceRequest generates requests for ProcessPairsByService
+func NewProcessPairsByServiceRequest(server string, id PathID) (*http.Request, error) {
 	var err error
 
 	var pathParam0 string
@@ -2274,7 +2273,7 @@ func NewLinksByRouterRequest(server string, id PathID) (*http.Request, error) {
 		return nil, err
 	}
 
-	operationPath := fmt.Sprintf("/api/v2alpha1/routers/%s/links", pathParam0)
+	operationPath := fmt.Sprintf("/api/v2alpha1/services/%s/processpairs", pathParam0)
 	if operationPath[0] == '/' {
 		operationPath = "." + operationPath
 	}
@@ -2593,21 +2592,6 @@ func WithBaseURL(baseURL string) ClientOption {
 
 // ClientWithResponsesInterface is the interface specification for the client with responses above.
 type ClientWithResponsesInterface interface {
-	// AddressesWithResponse request
-	AddressesWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*AddressesResponse, error)
-
-	// AddressByIDWithResponse request
-	AddressByIDWithResponse(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*AddressByIDResponse, error)
-
-	// ConnectionsByAddressWithResponse request
-	ConnectionsByAddressWithResponse(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*ConnectionsByAddressResponse, error)
-
-	// ProcessesByAddressWithResponse request
-	ProcessesByAddressWithResponse(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*ProcessesByAddressResponse, error)
-
-	// ProcessPairsByAddressWithResponse request
-	ProcessPairsByAddressWithResponse(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*ProcessPairsByAddressResponse, error)
-
 	// ApplicationflowsWithResponse request
 	ApplicationflowsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ApplicationflowsResponse, error)
 
@@ -2683,6 +2667,21 @@ type ClientWithResponsesInterface interface {
 	// LinksByRouterWithResponse request
 	LinksByRouterWithResponse(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*LinksByRouterResponse, error)
 
+	// ServicesWithResponse request
+	ServicesWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ServicesResponse, error)
+
+	// ServiceByIDWithResponse request
+	ServiceByIDWithResponse(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*ServiceByIDResponse, error)
+
+	// ConnectionsByServiceWithResponse request
+	ConnectionsByServiceWithResponse(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*ConnectionsByServiceResponse, error)
+
+	// ProcessesByServiceWithResponse request
+	ProcessesByServiceWithResponse(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*ProcessesByServiceResponse, error)
+
+	// ProcessPairsByServiceWithResponse request
+	ProcessPairsByServiceWithResponse(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*ProcessPairsByServiceResponse, error)
+
 	// SitepairsWithResponse request
 	SitepairsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*SitepairsResponse, error)
 
@@ -2708,121 +2707,6 @@ type ClientWithResponsesInterface interface {
 	RoutersBySiteWithResponse(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*RoutersBySiteResponse, error)
 }
 
-type AddressesResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *GetAddresses
-	JSON400      *ErrorBadRequest
-}
-
-// Status returns HTTPResponse.Status
-func (r AddressesResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r AddressesResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type AddressByIDResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *GetAddressByID
-	JSON404      *ErrorNotFound
-}
-
-// Status returns HTTPResponse.Status
-func (r AddressByIDResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r AddressByIDResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type ConnectionsByAddressResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *GetConnections
-	JSON404      *ErrorNotFound
-}
-
-// Status returns HTTPResponse.Status
-func (r ConnectionsByAddressResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r ConnectionsByAddressResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type ProcessesByAddressResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *GetProcesses
-	JSON400      *ErrorBadRequest
-}
-
-// Status returns HTTPResponse.Status
-func (r ProcessesByAddressResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r ProcessesByAddressResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type ProcessPairsByAddressResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *GetFlowAggregates
-	JSON404      *ErrorNotFound
-}
-
-// Status returns HTTPResponse.Status
-func (r ProcessPairsByAddressResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r ProcessPairsByAddressResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
 type ApplicationflowsResponse struct {
 	Body         []byte
 	HTTPResponse *http.Response
@@ -3394,15 +3278,15 @@ func (r LinksByRouterResponse) StatusCode() int {
 	return 0
 }
 
-type SitepairsResponse struct {
+type ServicesResponse struct {
 	Body         []byte
 	HTTPResponse *http.Response
-	JSON200      *GetFlowAggregates
+	JSON200      *GetServices
 	JSON400      *ErrorBadRequest
 }
 
 // Status returns HTTPResponse.Status
-func (r SitepairsResponse) Status() string {
+func (r ServicesResponse) Status() string {
 	if r.HTTPResponse != nil {
 		return r.HTTPResponse.Status
 	}
@@ -3410,7 +3294,122 @@ func (r SitepairsResponse) Status() string {
 }
 
 // StatusCode returns HTTPResponse.StatusCode
-func (r SitepairsResponse) StatusCode() int {
+func (r ServicesResponse) StatusCode() int {
+	if r.HTTPResponse != nil {
+		return r.HTTPResponse.StatusCode
+	}
+	return 0
+}
+
+type ServiceByIDResponse struct {
+	Body         []byte
+	HTTPResponse *http.Response
+	JSON200      *GetServiceByID
+	JSON404      *ErrorNotFound
+}
+
+// Status returns HTTPResponse.Status
+func (r ServiceByIDResponse) Status() string {
+	if r.HTTPResponse != nil {
+		return r.HTTPResponse.Status
+	}
+	return http.StatusText(0)
+}
+
+// StatusCode returns HTTPResponse.StatusCode
+func (r ServiceByIDResponse) StatusCode() int {
+	if r.HTTPResponse != nil {
+		return r.HTTPResponse.StatusCode
+	}
+	return 0
+}
+
+type ConnectionsByServiceResponse struct {
+	Body         []byte
+	HTTPResponse *http.Response
+	JSON200      *GetConnections
+	JSON404      *ErrorNotFound
+}
+
+// Status returns HTTPResponse.Status
+func (r ConnectionsByServiceResponse) Status() string {
+	if r.HTTPResponse != nil {
+		return r.HTTPResponse.Status
+	}
+	return http.StatusText(0)
+}
+
+// StatusCode returns HTTPResponse.StatusCode
+func (r ConnectionsByServiceResponse) StatusCode() int {
+	if r.HTTPResponse != nil {
+		return r.HTTPResponse.StatusCode
+	}
+	return 0
+}
+
+type ProcessesByServiceResponse struct {
+	Body         []byte
+	HTTPResponse *http.Response
+	JSON200      *GetProcesses
+	JSON400      *ErrorBadRequest
+}
+
+// Status returns HTTPResponse.Status
+func (r ProcessesByServiceResponse) Status() string {
+	if r.HTTPResponse != nil {
+		return r.HTTPResponse.Status
+	}
+	return http.StatusText(0)
+}
+
+// StatusCode returns HTTPResponse.StatusCode
+func (r ProcessesByServiceResponse) StatusCode() int {
+	if r.HTTPResponse != nil {
+		return r.HTTPResponse.StatusCode
+	}
+	return 0
+}
+
+type ProcessPairsByServiceResponse struct {
+	Body         []byte
+	HTTPResponse *http.Response
+	JSON200      *GetFlowAggregates
+	JSON404      *ErrorNotFound
+}
+
+// Status returns HTTPResponse.Status
+func (r ProcessPairsByServiceResponse) Status() string {
+	if r.HTTPResponse != nil {
+		return r.HTTPResponse.Status
+	}
+	return http.StatusText(0)
+}
+
+// StatusCode returns HTTPResponse.StatusCode
+func (r ProcessPairsByServiceResponse) StatusCode() int {
+	if r.HTTPResponse != nil {
+		return r.HTTPResponse.StatusCode
+	}
+	return 0
+}
+
+type SitepairsResponse struct {
+	Body         []byte
+	HTTPResponse *http.Response
+	JSON200      *GetFlowAggregates
+	JSON400      *ErrorBadRequest
+}
+
+// Status returns HTTPResponse.Status
+func (r SitepairsResponse) Status() string {
+	if r.HTTPResponse != nil {
+		return r.HTTPResponse.Status
+	}
+	return http.StatusText(0)
+}
+
+// StatusCode returns HTTPResponse.StatusCode
+func (r SitepairsResponse) StatusCode() int {
 	if r.HTTPResponse != nil {
 		return r.HTTPResponse.StatusCode
 	}
@@ -3576,51 +3575,6 @@ func (r RoutersBySiteResponse) StatusCode() int {
 	return 0
 }
 
-// AddressesWithResponse request returning *AddressesResponse
-func (c *ClientWithResponses) AddressesWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*AddressesResponse, error) {
-	rsp, err := c.Addresses(ctx, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseAddressesResponse(rsp)
-}
-
-// AddressByIDWithResponse request returning *AddressByIDResponse
-func (c *ClientWithResponses) AddressByIDWithResponse(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*AddressByIDResponse, error) {
-	rsp, err := c.AddressByID(ctx, id, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseAddressByIDResponse(rsp)
-}
-
-// ConnectionsByAddressWithResponse request returning *ConnectionsByAddressResponse
-func (c *ClientWithResponses) ConnectionsByAddressWithResponse(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*ConnectionsByAddressResponse, error) {
-	rsp, err := c.ConnectionsByAddress(ctx, id, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseConnectionsByAddressResponse(rsp)
-}
-
-// ProcessesByAddressWithResponse request returning *ProcessesByAddressResponse
-func (c *ClientWithResponses) ProcessesByAddressWithResponse(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*ProcessesByAddressResponse, error) {
-	rsp, err := c.ProcessesByAddress(ctx, id, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseProcessesByAddressResponse(rsp)
-}
-
-// ProcessPairsByAddressWithResponse request returning *ProcessPairsByAddressResponse
-func (c *ClientWithResponses) ProcessPairsByAddressWithResponse(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*ProcessPairsByAddressResponse, error) {
-	rsp, err := c.ProcessPairsByAddress(ctx, id, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseProcessPairsByAddressResponse(rsp)
-}
-
 // ApplicationflowsWithResponse request returning *ApplicationflowsResponse
 func (c *ClientWithResponses) ApplicationflowsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ApplicationflowsResponse, error) {
 	rsp, err := c.Applicationflows(ctx, reqEditors...)
@@ -3846,6 +3800,51 @@ func (c *ClientWithResponses) LinksByRouterWithResponse(ctx context.Context, id
 	return ParseLinksByRouterResponse(rsp)
 }
 
+// ServicesWithResponse request returning *ServicesResponse
+func (c *ClientWithResponses) ServicesWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ServicesResponse, error) {
+	rsp, err := c.Services(ctx, reqEditors...)
+	if err != nil {
+		return nil, err
+	}
+	return ParseServicesResponse(rsp)
+}
+
+// ServiceByIDWithResponse request returning *ServiceByIDResponse
+func (c *ClientWithResponses) ServiceByIDWithResponse(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*ServiceByIDResponse, error) {
+	rsp, err := c.ServiceByID(ctx, id, reqEditors...)
+	if err != nil {
+		return nil, err
+	}
+	return ParseServiceByIDResponse(rsp)
+}
+
+// ConnectionsByServiceWithResponse request returning *ConnectionsByServiceResponse
+func (c *ClientWithResponses) ConnectionsByServiceWithResponse(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*ConnectionsByServiceResponse, error) {
+	rsp, err := c.ConnectionsByService(ctx, id, reqEditors...)
+	if err != nil {
+		return nil, err
+	}
+	return ParseConnectionsByServiceResponse(rsp)
+}
+
+// ProcessesByServiceWithResponse request returning *ProcessesByServiceResponse
+func (c *ClientWithResponses) ProcessesByServiceWithResponse(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*ProcessesByServiceResponse, error) {
+	rsp, err := c.ProcessesByService(ctx, id, reqEditors...)
+	if err != nil {
+		return nil, err
+	}
+	return ParseProcessesByServiceResponse(rsp)
+}
+
+// ProcessPairsByServiceWithResponse request returning *ProcessPairsByServiceResponse
+func (c *ClientWithResponses) ProcessPairsByServiceWithResponse(ctx context.Context, id PathID, reqEditors ...RequestEditorFn) (*ProcessPairsByServiceResponse, error) {
+	rsp, err := c.ProcessPairsByService(ctx, id, reqEditors...)
+	if err != nil {
+		return nil, err
+	}
+	return ParseProcessPairsByServiceResponse(rsp)
+}
+
 // SitepairsWithResponse request returning *SitepairsResponse
 func (c *ClientWithResponses) SitepairsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*SitepairsResponse, error) {
 	rsp, err := c.Sitepairs(ctx, reqEditors...)
@@ -3918,81 +3917,41 @@ func (c *ClientWithResponses) RoutersBySiteWithResponse(ctx context.Context, id
 	return ParseRoutersBySiteResponse(rsp)
 }
 
-// ParseAddressesResponse parses an HTTP response from a AddressesWithResponse call
-func ParseAddressesResponse(rsp *http.Response) (*AddressesResponse, error) {
-	bodyBytes, err := io.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &AddressesResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetAddresses
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON200 = &dest
-
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400:
-		var dest ErrorBadRequest
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON400 = &dest
-
-	}
-
-	return response, nil
-}
-
-// ParseAddressByIDResponse parses an HTTP response from a AddressByIDWithResponse call
-func ParseAddressByIDResponse(rsp *http.Response) (*AddressByIDResponse, error) {
+// ParseApplicationflowsResponse parses an HTTP response from a ApplicationflowsWithResponse call
+func ParseApplicationflowsResponse(rsp *http.Response) (*ApplicationflowsResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &AddressByIDResponse{
+	response := &ApplicationflowsResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetAddressByID
+		var dest GetApplicationFlows
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
 		response.JSON200 = &dest
 
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404:
-		var dest ErrorNotFound
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON404 = &dest
-
 	}
 
 	return response, nil
 }
 
-// ParseConnectionsByAddressResponse parses an HTTP response from a ConnectionsByAddressWithResponse call
-func ParseConnectionsByAddressResponse(rsp *http.Response) (*ConnectionsByAddressResponse, error) {
+// ParseConnectionsResponse parses an HTTP response from a ConnectionsWithResponse call
+func ParseConnectionsResponse(rsp *http.Response) (*ConnectionsResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &ConnectionsByAddressResponse{
+	response := &ConnectionsResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
@@ -4005,34 +3964,27 @@ func ParseConnectionsByAddressResponse(rsp *http.Response) (*ConnectionsByAddres
 		}
 		response.JSON200 = &dest
 
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404:
-		var dest ErrorNotFound
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON404 = &dest
-
 	}
 
 	return response, nil
 }
 
-// ParseProcessesByAddressResponse parses an HTTP response from a ProcessesByAddressWithResponse call
-func ParseProcessesByAddressResponse(rsp *http.Response) (*ProcessesByAddressResponse, error) {
+// ParseConnectorsResponse parses an HTTP response from a ConnectorsWithResponse call
+func ParseConnectorsResponse(rsp *http.Response) (*ConnectorsResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &ProcessesByAddressResponse{
+	response := &ConnectorsResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetProcesses
+		var dest GetConnectors
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
@@ -4050,22 +4002,22 @@ func ParseProcessesByAddressResponse(rsp *http.Response) (*ProcessesByAddressRes
 	return response, nil
 }
 
-// ParseProcessPairsByAddressResponse parses an HTTP response from a ProcessPairsByAddressWithResponse call
-func ParseProcessPairsByAddressResponse(rsp *http.Response) (*ProcessPairsByAddressResponse, error) {
+// ParseConnectorByIDResponse parses an HTTP response from a ConnectorByIDWithResponse call
+func ParseConnectorByIDResponse(rsp *http.Response) (*ConnectorByIDResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &ProcessPairsByAddressResponse{
+	response := &ConnectorByIDResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetFlowAggregates
+		var dest GetConnectorByID
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
@@ -4083,74 +4035,81 @@ func ParseProcessPairsByAddressResponse(rsp *http.Response) (*ProcessPairsByAddr
 	return response, nil
 }
 
-// ParseApplicationflowsResponse parses an HTTP response from a ApplicationflowsWithResponse call
-func ParseApplicationflowsResponse(rsp *http.Response) (*ApplicationflowsResponse, error) {
+// ParseHostsResponse parses an HTTP response from a HostsWithResponse call
+func ParseHostsResponse(rsp *http.Response) (*HostsResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &ApplicationflowsResponse{
+	response := &HostsResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetApplicationFlows
+	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 410:
+		var dest NotSupported
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
-		response.JSON200 = &dest
+		response.JSON410 = &dest
 
 	}
 
 	return response, nil
 }
 
-// ParseConnectionsResponse parses an HTTP response from a ConnectionsWithResponse call
-func ParseConnectionsResponse(rsp *http.Response) (*ConnectionsResponse, error) {
+// ParseHostsByIDResponse parses an HTTP response from a HostsByIDWithResponse call
+func ParseHostsByIDResponse(rsp *http.Response) (*HostsByIDResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &ConnectionsResponse{
+	response := &HostsByIDResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetConnections
+		var dest NotSupported
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
 		response.JSON200 = &dest
 
+	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404:
+		var dest ErrorNotFound
+		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
+			return nil, err
+		}
+		response.JSON404 = &dest
+
 	}
 
 	return response, nil
 }
 
-// ParseConnectorsResponse parses an HTTP response from a ConnectorsWithResponse call
-func ParseConnectorsResponse(rsp *http.Response) (*ConnectorsResponse, error) {
+// ParseLinksResponse parses an HTTP response from a LinksWithResponse call
+func ParseLinksResponse(rsp *http.Response) (*LinksResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &ConnectorsResponse{
+	response := &LinksResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetConnectors
+		var dest GetLinks
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
@@ -4168,22 +4127,22 @@ func ParseConnectorsResponse(rsp *http.Response) (*ConnectorsResponse, error) {
 	return response, nil
 }
 
-// ParseConnectorByIDResponse parses an HTTP response from a ConnectorByIDWithResponse call
-func ParseConnectorByIDResponse(rsp *http.Response) (*ConnectorByIDResponse, error) {
+// ParseLinkByIDResponse parses an HTTP response from a LinkByIDWithResponse call
+func ParseLinkByIDResponse(rsp *http.Response) (*LinkByIDResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &ConnectorByIDResponse{
+	response := &LinkByIDResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetConnectorByID
+		var dest GetLinkByID
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
@@ -4201,48 +4160,55 @@ func ParseConnectorByIDResponse(rsp *http.Response) (*ConnectorByIDResponse, err
 	return response, nil
 }
 
-// ParseHostsResponse parses an HTTP response from a HostsWithResponse call
-func ParseHostsResponse(rsp *http.Response) (*HostsResponse, error) {
+// ParseListenersResponse parses an HTTP response from a ListenersWithResponse call
+func ParseListenersResponse(rsp *http.Response) (*ListenersResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &HostsResponse{
+	response := &ListenersResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 410:
-		var dest NotSupported
+	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
+		var dest GetListeners
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
-		response.JSON410 = &dest
+		response.JSON200 = &dest
+
+	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400:
+		var dest ErrorBadRequest
+		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
+			return nil, err
+		}
+		response.JSON400 = &dest
 
 	}
 
 	return response, nil
 }
 
-// ParseHostsByIDResponse parses an HTTP response from a HostsByIDWithResponse call
-func ParseHostsByIDResponse(rsp *http.Response) (*HostsByIDResponse, error) {
+// ParseListenerByIDResponse parses an HTTP response from a ListenerByIDWithResponse call
+func ParseListenerByIDResponse(rsp *http.Response) (*ListenerByIDResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &HostsByIDResponse{
+	response := &ListenerByIDResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest NotSupported
+		var dest GetListenerByID
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
@@ -4260,22 +4226,22 @@ func ParseHostsByIDResponse(rsp *http.Response) (*HostsByIDResponse, error) {
 	return response, nil
 }
 
-// ParseLinksResponse parses an HTTP response from a LinksWithResponse call
-func ParseLinksResponse(rsp *http.Response) (*LinksResponse, error) {
+// ParseProcessesResponse parses an HTTP response from a ProcessesWithResponse call
+func ParseProcessesResponse(rsp *http.Response) (*ProcessesResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &LinksResponse{
+	response := &ProcessesResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetLinks
+		var dest GetProcesses
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
@@ -4293,22 +4259,22 @@ func ParseLinksResponse(rsp *http.Response) (*LinksResponse, error) {
 	return response, nil
 }
 
-// ParseLinkByIDResponse parses an HTTP response from a LinkByIDWithResponse call
-func ParseLinkByIDResponse(rsp *http.Response) (*LinkByIDResponse, error) {
+// ParseProcessByIdResponse parses an HTTP response from a ProcessByIdWithResponse call
+func ParseProcessByIdResponse(rsp *http.Response) (*ProcessByIdResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &LinkByIDResponse{
+	response := &ProcessByIdResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetLinkByID
+		var dest GetProcessByID
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
@@ -4326,22 +4292,22 @@ func ParseLinkByIDResponse(rsp *http.Response) (*LinkByIDResponse, error) {
 	return response, nil
 }
 
-// ParseListenersResponse parses an HTTP response from a ListenersWithResponse call
-func ParseListenersResponse(rsp *http.Response) (*ListenersResponse, error) {
+// ParseProcessgrouppairsResponse parses an HTTP response from a ProcessgrouppairsWithResponse call
+func ParseProcessgrouppairsResponse(rsp *http.Response) (*ProcessgrouppairsResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &ListenersResponse{
+	response := &ProcessgrouppairsResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetListeners
+		var dest GetFlowAggregates
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
@@ -4359,22 +4325,22 @@ func ParseListenersResponse(rsp *http.Response) (*ListenersResponse, error) {
 	return response, nil
 }
 
-// ParseListenerByIDResponse parses an HTTP response from a ListenerByIDWithResponse call
-func ParseListenerByIDResponse(rsp *http.Response) (*ListenerByIDResponse, error) {
+// ParseProcessgrouppairByIDResponse parses an HTTP response from a ProcessgrouppairByIDWithResponse call
+func ParseProcessgrouppairByIDResponse(rsp *http.Response) (*ProcessgrouppairByIDResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &ListenerByIDResponse{
+	response := &ProcessgrouppairByIDResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetListenerByID
+		var dest GetFlowAggregateByID
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
@@ -4392,22 +4358,22 @@ func ParseListenerByIDResponse(rsp *http.Response) (*ListenerByIDResponse, error
 	return response, nil
 }
 
-// ParseProcessesResponse parses an HTTP response from a ProcessesWithResponse call
-func ParseProcessesResponse(rsp *http.Response) (*ProcessesResponse, error) {
+// ParseProcessgroupsResponse parses an HTTP response from a ProcessgroupsWithResponse call
+func ParseProcessgroupsResponse(rsp *http.Response) (*ProcessgroupsResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &ProcessesResponse{
+	response := &ProcessgroupsResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetProcesses
+		var dest GetProcessGroups
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
@@ -4425,22 +4391,22 @@ func ParseProcessesResponse(rsp *http.Response) (*ProcessesResponse, error) {
 	return response, nil
 }
 
-// ParseProcessByIdResponse parses an HTTP response from a ProcessByIdWithResponse call
-func ParseProcessByIdResponse(rsp *http.Response) (*ProcessByIdResponse, error) {
+// ParseProcessgroupByIDResponse parses an HTTP response from a ProcessgroupByIDWithResponse call
+func ParseProcessgroupByIDResponse(rsp *http.Response) (*ProcessgroupByIDResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &ProcessByIdResponse{
+	response := &ProcessgroupByIDResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetProcessByID
+		var dest GetProcessGroupByID
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
@@ -4458,15 +4424,15 @@ func ParseProcessByIdResponse(rsp *http.Response) (*ProcessByIdResponse, error)
 	return response, nil
 }
 
-// ParseProcessgrouppairsResponse parses an HTTP response from a ProcessgrouppairsWithResponse call
-func ParseProcessgrouppairsResponse(rsp *http.Response) (*ProcessgrouppairsResponse, error) {
+// ParseProcesspairsResponse parses an HTTP response from a ProcesspairsWithResponse call
+func ParseProcesspairsResponse(rsp *http.Response) (*ProcesspairsResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &ProcessgrouppairsResponse{
+	response := &ProcesspairsResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
@@ -4491,15 +4457,15 @@ func ParseProcessgrouppairsResponse(rsp *http.Response) (*ProcessgrouppairsRespo
 	return response, nil
 }
 
-// ParseProcessgrouppairByIDResponse parses an HTTP response from a ProcessgrouppairByIDWithResponse call
-func ParseProcessgrouppairByIDResponse(rsp *http.Response) (*ProcessgrouppairByIDResponse, error) {
+// ParseProcesspairByIDResponse parses an HTTP response from a ProcesspairByIDWithResponse call
+func ParseProcesspairByIDResponse(rsp *http.Response) (*ProcesspairByIDResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &ProcessgrouppairByIDResponse{
+	response := &ProcesspairByIDResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
@@ -4524,22 +4490,22 @@ func ParseProcessgrouppairByIDResponse(rsp *http.Response) (*ProcessgrouppairByI
 	return response, nil
 }
 
-// ParseProcessgroupsResponse parses an HTTP response from a ProcessgroupsWithResponse call
-func ParseProcessgroupsResponse(rsp *http.Response) (*ProcessgroupsResponse, error) {
+// ParseRouteraccessResponse parses an HTTP response from a RouteraccessWithResponse call
+func ParseRouteraccessResponse(rsp *http.Response) (*RouteraccessResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &ProcessgroupsResponse{
+	response := &RouteraccessResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetProcessGroups
+		var dest GetRouterAccess
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
@@ -4557,22 +4523,22 @@ func ParseProcessgroupsResponse(rsp *http.Response) (*ProcessgroupsResponse, err
 	return response, nil
 }
 
-// ParseProcessgroupByIDResponse parses an HTTP response from a ProcessgroupByIDWithResponse call
-func ParseProcessgroupByIDResponse(rsp *http.Response) (*ProcessgroupByIDResponse, error) {
+// ParseRouteraccessByIDResponse parses an HTTP response from a RouteraccessByIDWithResponse call
+func ParseRouteraccessByIDResponse(rsp *http.Response) (*RouteraccessByIDResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &ProcessgroupByIDResponse{
+	response := &RouteraccessByIDResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetProcessGroupByID
+		var dest GetRouterAccessByID
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
@@ -4590,22 +4556,22 @@ func ParseProcessgroupByIDResponse(rsp *http.Response) (*ProcessgroupByIDRespons
 	return response, nil
 }
 
-// ParseProcesspairsResponse parses an HTTP response from a ProcesspairsWithResponse call
-func ParseProcesspairsResponse(rsp *http.Response) (*ProcesspairsResponse, error) {
+// ParseRouterlinksResponse parses an HTTP response from a RouterlinksWithResponse call
+func ParseRouterlinksResponse(rsp *http.Response) (*RouterlinksResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &ProcesspairsResponse{
+	response := &RouterlinksResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetFlowAggregates
+		var dest GetRouterLinks
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
@@ -4623,22 +4589,22 @@ func ParseProcesspairsResponse(rsp *http.Response) (*ProcesspairsResponse, error
 	return response, nil
 }
 
-// ParseProcesspairByIDResponse parses an HTTP response from a ProcesspairByIDWithResponse call
-func ParseProcesspairByIDResponse(rsp *http.Response) (*ProcesspairByIDResponse, error) {
+// ParseRouterlinkByIDResponse parses an HTTP response from a RouterlinkByIDWithResponse call
+func ParseRouterlinkByIDResponse(rsp *http.Response) (*RouterlinkByIDResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &ProcesspairByIDResponse{
+	response := &RouterlinkByIDResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetFlowAggregateByID
+		var dest GetRouterLinkByID
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
@@ -4656,22 +4622,22 @@ func ParseProcesspairByIDResponse(rsp *http.Response) (*ProcesspairByIDResponse,
 	return response, nil
 }
 
-// ParseRouteraccessResponse parses an HTTP response from a RouteraccessWithResponse call
-func ParseRouteraccessResponse(rsp *http.Response) (*RouteraccessResponse, error) {
+// ParseRoutersResponse parses an HTTP response from a RoutersWithResponse call
+func ParseRoutersResponse(rsp *http.Response) (*RoutersResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &RouteraccessResponse{
+	response := &RoutersResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetRouterAccess
+		var dest GetRouters
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
@@ -4689,22 +4655,22 @@ func ParseRouteraccessResponse(rsp *http.Response) (*RouteraccessResponse, error
 	return response, nil
 }
 
-// ParseRouteraccessByIDResponse parses an HTTP response from a RouteraccessByIDWithResponse call
-func ParseRouteraccessByIDResponse(rsp *http.Response) (*RouteraccessByIDResponse, error) {
+// ParseRouterByIDResponse parses an HTTP response from a RouterByIDWithResponse call
+func ParseRouterByIDResponse(rsp *http.Response) (*RouterByIDResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &RouteraccessByIDResponse{
+	response := &RouterByIDResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetRouterAccessByID
+		var dest GetRouterByID
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
@@ -4722,22 +4688,48 @@ func ParseRouteraccessByIDResponse(rsp *http.Response) (*RouteraccessByIDRespons
 	return response, nil
 }
 
-// ParseRouterlinksResponse parses an HTTP response from a RouterlinksWithResponse call
-func ParseRouterlinksResponse(rsp *http.Response) (*RouterlinksResponse, error) {
+// ParseLinksByRouterResponse parses an HTTP response from a LinksByRouterWithResponse call
+func ParseLinksByRouterResponse(rsp *http.Response) (*LinksByRouterResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &RouterlinksResponse{
+	response := &LinksByRouterResponse{
+		Body:         bodyBytes,
+		HTTPResponse: rsp,
+	}
+
+	switch {
+	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 410:
+		var dest NotSupported
+		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
+			return nil, err
+		}
+		response.JSON410 = &dest
+
+	}
+
+	return response, nil
+}
+
+// ParseServicesResponse parses an HTTP response from a ServicesWithResponse call
+func ParseServicesResponse(rsp *http.Response) (*ServicesResponse, error) {
+	bodyBytes, err := io.ReadAll(rsp.Body)
+	defer func() { _ = rsp.Body.Close() }()
+	if err != nil {
+		return nil, err
+	}
+
+	response := &ServicesResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetRouterLinks
+		var dest GetServices
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
@@ -4755,22 +4747,22 @@ func ParseRouterlinksResponse(rsp *http.Response) (*RouterlinksResponse, error)
 	return response, nil
 }
 
-// ParseRouterlinkByIDResponse parses an HTTP response from a RouterlinkByIDWithResponse call
-func ParseRouterlinkByIDResponse(rsp *http.Response) (*RouterlinkByIDResponse, error) {
+// ParseServiceByIDResponse parses an HTTP response from a ServiceByIDWithResponse call
+func ParseServiceByIDResponse(rsp *http.Response) (*ServiceByIDResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &RouterlinkByIDResponse{
+	response := &ServiceByIDResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetRouterLinkByID
+		var dest GetServiceByID
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
@@ -4788,92 +4780,99 @@ func ParseRouterlinkByIDResponse(rsp *http.Response) (*RouterlinkByIDResponse, e
 	return response, nil
 }
 
-// ParseRoutersResponse parses an HTTP response from a RoutersWithResponse call
-func ParseRoutersResponse(rsp *http.Response) (*RoutersResponse, error) {
+// ParseConnectionsByServiceResponse parses an HTTP response from a ConnectionsByServiceWithResponse call
+func ParseConnectionsByServiceResponse(rsp *http.Response) (*ConnectionsByServiceResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &RoutersResponse{
+	response := &ConnectionsByServiceResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetRouters
+		var dest GetConnections
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
 		response.JSON200 = &dest
 
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400:
-		var dest ErrorBadRequest
+	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404:
+		var dest ErrorNotFound
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
-		response.JSON400 = &dest
+		response.JSON404 = &dest
 
 	}
 
 	return response, nil
 }
 
-// ParseRouterByIDResponse parses an HTTP response from a RouterByIDWithResponse call
-func ParseRouterByIDResponse(rsp *http.Response) (*RouterByIDResponse, error) {
+// ParseProcessesByServiceResponse parses an HTTP response from a ProcessesByServiceWithResponse call
+func ParseProcessesByServiceResponse(rsp *http.Response) (*ProcessesByServiceResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &RouterByIDResponse{
+	response := &ProcessesByServiceResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
 	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest GetRouterByID
+		var dest GetProcesses
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
 		response.JSON200 = &dest
 
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404:
-		var dest ErrorNotFound
+	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400:
+		var dest ErrorBadRequest
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
-		response.JSON404 = &dest
+		response.JSON400 = &dest
 
 	}
 
 	return response, nil
 }
 
-// ParseLinksByRouterResponse parses an HTTP response from a LinksByRouterWithResponse call
-func ParseLinksByRouterResponse(rsp *http.Response) (*LinksByRouterResponse, error) {
+// ParseProcessPairsByServiceResponse parses an HTTP response from a ProcessPairsByServiceWithResponse call
+func ParseProcessPairsByServiceResponse(rsp *http.Response) (*ProcessPairsByServiceResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
 	defer func() { _ = rsp.Body.Close() }()
 	if err != nil {
 		return nil, err
 	}
 
-	response := &LinksByRouterResponse{
+	response := &ProcessPairsByServiceResponse{
 		Body:         bodyBytes,
 		HTTPResponse: rsp,
 	}
 
 	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 410:
-		var dest NotSupported
+	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
+		var dest GetFlowAggregates
 		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
 			return nil, err
 		}
-		response.JSON410 = &dest
+		response.JSON200 = &dest
+
+	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404:
+		var dest ErrorNotFound
+		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
+			return nil, err
+		}
+		response.JSON404 = &dest
 
 	}
 
@@ -5133,21 +5132,6 @@ func ParseRoutersBySiteResponse(rsp *http.Response) (*RoutersBySiteResponse, err
 // ServerInterface represents all server handlers.
 type ServerInterface interface {
 
-	// (GET /api/v2alpha1/addresses)
-	Addresses(w http.ResponseWriter, r *http.Request)
-
-	// (GET /api/v2alpha1/addresses/{id})
-	AddressByID(w http.ResponseWriter, r *http.Request, id PathID)
-
-	// (GET /api/v2alpha1/addresses/{id}/connections)
-	ConnectionsByAddress(w http.ResponseWriter, r *http.Request, id PathID)
-
-	// (GET /api/v2alpha1/addresses/{id}/processes)
-	ProcessesByAddress(w http.ResponseWriter, r *http.Request, id PathID)
-
-	// (GET /api/v2alpha1/addresses/{id}/processpairs)
-	ProcessPairsByAddress(w http.ResponseWriter, r *http.Request, id PathID)
-
 	// (GET /api/v2alpha1/applicationflows)
 	Applicationflows(w http.ResponseWriter, r *http.Request)
 
@@ -5223,6 +5207,21 @@ type ServerInterface interface {
 	// (GET /api/v2alpha1/routers/{id}/links)
 	LinksByRouter(w http.ResponseWriter, r *http.Request, id PathID)
 
+	// (GET /api/v2alpha1/services)
+	Services(w http.ResponseWriter, r *http.Request)
+
+	// (GET /api/v2alpha1/services/{id})
+	ServiceByID(w http.ResponseWriter, r *http.Request, id PathID)
+
+	// (GET /api/v2alpha1/services/{id}/connections)
+	ConnectionsByService(w http.ResponseWriter, r *http.Request, id PathID)
+
+	// (GET /api/v2alpha1/services/{id}/processes)
+	ProcessesByService(w http.ResponseWriter, r *http.Request, id PathID)
+
+	// (GET /api/v2alpha1/services/{id}/processpairs)
+	ProcessPairsByService(w http.ResponseWriter, r *http.Request, id PathID)
+
 	// (GET /api/v2alpha1/sitepairs)
 	Sitepairs(w http.ResponseWriter, r *http.Request)
 
@@ -5257,125 +5256,6 @@ type ServerInterfaceWrapper struct {
 
 type MiddlewareFunc func(http.Handler) http.Handler
 
-// Addresses operation middleware
-func (siw *ServerInterfaceWrapper) Addresses(w http.ResponseWriter, r *http.Request) {
-	ctx := r.Context()
-
-	handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		siw.Handler.Addresses(w, r)
-	}))
-
-	for _, middleware := range siw.HandlerMiddlewares {
-		handler = middleware(handler)
-	}
-
-	handler.ServeHTTP(w, r.WithContext(ctx))
-}
-
-// AddressByID operation middleware
-func (siw *ServerInterfaceWrapper) AddressByID(w http.ResponseWriter, r *http.Request) {
-	ctx := r.Context()
-
-	var err error
-
-	// ------------- Path parameter "id" -------------
-	var id PathID
-
-	err = runtime.BindStyledParameterWithOptions("simple", "id", mux.Vars(r)["id"], &id, runtime.BindStyledParameterOptions{Explode: false, Required: true})
-	if err != nil {
-		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err})
-		return
-	}
-
-	handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		siw.Handler.AddressByID(w, r, id)
-	}))
-
-	for _, middleware := range siw.HandlerMiddlewares {
-		handler = middleware(handler)
-	}
-
-	handler.ServeHTTP(w, r.WithContext(ctx))
-}
-
-// ConnectionsByAddress operation middleware
-func (siw *ServerInterfaceWrapper) ConnectionsByAddress(w http.ResponseWriter, r *http.Request) {
-	ctx := r.Context()
-
-	var err error
-
-	// ------------- Path parameter "id" -------------
-	var id PathID
-
-	err = runtime.BindStyledParameterWithOptions("simple", "id", mux.Vars(r)["id"], &id, runtime.BindStyledParameterOptions{Explode: false, Required: true})
-	if err != nil {
-		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err})
-		return
-	}
-
-	handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		siw.Handler.ConnectionsByAddress(w, r, id)
-	}))
-
-	for _, middleware := range siw.HandlerMiddlewares {
-		handler = middleware(handler)
-	}
-
-	handler.ServeHTTP(w, r.WithContext(ctx))
-}
-
-// ProcessesByAddress operation middleware
-func (siw *ServerInterfaceWrapper) ProcessesByAddress(w http.ResponseWriter, r *http.Request) {
-	ctx := r.Context()
-
-	var err error
-
-	// ------------- Path parameter "id" -------------
-	var id PathID
-
-	err = runtime.BindStyledParameterWithOptions("simple", "id", mux.Vars(r)["id"], &id, runtime.BindStyledParameterOptions{Explode: false, Required: true})
-	if err != nil {
-		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err})
-		return
-	}
-
-	handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		siw.Handler.ProcessesByAddress(w, r, id)
-	}))
-
-	for _, middleware := range siw.HandlerMiddlewares {
-		handler = middleware(handler)
-	}
-
-	handler.ServeHTTP(w, r.WithContext(ctx))
-}
-
-// ProcessPairsByAddress operation middleware
-func (siw *ServerInterfaceWrapper) ProcessPairsByAddress(w http.ResponseWriter, r *http.Request) {
-	ctx := r.Context()
-
-	var err error
-
-	// ------------- Path parameter "id" -------------
-	var id PathID
-
-	err = runtime.BindStyledParameterWithOptions("simple", "id", mux.Vars(r)["id"], &id, runtime.BindStyledParameterOptions{Explode: false, Required: true})
-	if err != nil {
-		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err})
-		return
-	}
-
-	handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		siw.Handler.ProcessPairsByAddress(w, r, id)
-	}))
-
-	for _, middleware := range siw.HandlerMiddlewares {
-		handler = middleware(handler)
-	}
-
-	handler.ServeHTTP(w, r.WithContext(ctx))
-}
-
 // Applicationflows operation middleware
 func (siw *ServerInterfaceWrapper) Applicationflows(w http.ResponseWriter, r *http.Request) {
 	ctx := r.Context()
@@ -5883,6 +5763,125 @@ func (siw *ServerInterfaceWrapper) LinksByRouter(w http.ResponseWriter, r *http.
 	handler.ServeHTTP(w, r.WithContext(ctx))
 }
 
+// Services operation middleware
+func (siw *ServerInterfaceWrapper) Services(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+
+	handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		siw.Handler.Services(w, r)
+	}))
+
+	for _, middleware := range siw.HandlerMiddlewares {
+		handler = middleware(handler)
+	}
+
+	handler.ServeHTTP(w, r.WithContext(ctx))
+}
+
+// ServiceByID operation middleware
+func (siw *ServerInterfaceWrapper) ServiceByID(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+
+	var err error
+
+	// ------------- Path parameter "id" -------------
+	var id PathID
+
+	err = runtime.BindStyledParameterWithOptions("simple", "id", mux.Vars(r)["id"], &id, runtime.BindStyledParameterOptions{Explode: false, Required: true})
+	if err != nil {
+		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err})
+		return
+	}
+
+	handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		siw.Handler.ServiceByID(w, r, id)
+	}))
+
+	for _, middleware := range siw.HandlerMiddlewares {
+		handler = middleware(handler)
+	}
+
+	handler.ServeHTTP(w, r.WithContext(ctx))
+}
+
+// ConnectionsByService operation middleware
+func (siw *ServerInterfaceWrapper) ConnectionsByService(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+
+	var err error
+
+	// ------------- Path parameter "id" -------------
+	var id PathID
+
+	err = runtime.BindStyledParameterWithOptions("simple", "id", mux.Vars(r)["id"], &id, runtime.BindStyledParameterOptions{Explode: false, Required: true})
+	if err != nil {
+		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err})
+		return
+	}
+
+	handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		siw.Handler.ConnectionsByService(w, r, id)
+	}))
+
+	for _, middleware := range siw.HandlerMiddlewares {
+		handler = middleware(handler)
+	}
+
+	handler.ServeHTTP(w, r.WithContext(ctx))
+}
+
+// ProcessesByService operation middleware
+func (siw *ServerInterfaceWrapper) ProcessesByService(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+
+	var err error
+
+	// ------------- Path parameter "id" -------------
+	var id PathID
+
+	err = runtime.BindStyledParameterWithOptions("simple", "id", mux.Vars(r)["id"], &id, runtime.BindStyledParameterOptions{Explode: false, Required: true})
+	if err != nil {
+		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err})
+		return
+	}
+
+	handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		siw.Handler.ProcessesByService(w, r, id)
+	}))
+
+	for _, middleware := range siw.HandlerMiddlewares {
+		handler = middleware(handler)
+	}
+
+	handler.ServeHTTP(w, r.WithContext(ctx))
+}
+
+// ProcessPairsByService operation middleware
+func (siw *ServerInterfaceWrapper) ProcessPairsByService(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+
+	var err error
+
+	// ------------- Path parameter "id" -------------
+	var id PathID
+
+	err = runtime.BindStyledParameterWithOptions("simple", "id", mux.Vars(r)["id"], &id, runtime.BindStyledParameterOptions{Explode: false, Required: true})
+	if err != nil {
+		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err})
+		return
+	}
+
+	handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		siw.Handler.ProcessPairsByService(w, r, id)
+	}))
+
+	for _, middleware := range siw.HandlerMiddlewares {
+		handler = middleware(handler)
+	}
+
+	handler.ServeHTTP(w, r.WithContext(ctx))
+}
+
 // Sitepairs operation middleware
 func (siw *ServerInterfaceWrapper) Sitepairs(w http.ResponseWriter, r *http.Request) {
 	ctx := r.Context()
@@ -6182,16 +6181,6 @@ func HandlerWithOptions(si ServerInterface, options GorillaServerOptions) http.H
 		ErrorHandlerFunc:   options.ErrorHandlerFunc,
 	}
 
-	r.HandleFunc(options.BaseURL+"/api/v2alpha1/addresses", wrapper.Addresses).Methods("GET")
-
-	r.HandleFunc(options.BaseURL+"/api/v2alpha1/addresses/{id}", wrapper.AddressByID).Methods("GET")
-
-	r.HandleFunc(options.BaseURL+"/api/v2alpha1/addresses/{id}/connections", wrapper.ConnectionsByAddress).Methods("GET")
-
-	r.HandleFunc(options.BaseURL+"/api/v2alpha1/addresses/{id}/processes", wrapper.ProcessesByAddress).Methods("GET")
-
-	r.HandleFunc(options.BaseURL+"/api/v2alpha1/addresses/{id}/processpairs", wrapper.ProcessPairsByAddress).Methods("GET")
-
 	r.HandleFunc(options.BaseURL+"/api/v2alpha1/applicationflows", wrapper.Applicationflows).Methods("GET")
 
 	r.HandleFunc(options.BaseURL+"/api/v2alpha1/connections", wrapper.Connections).Methods("GET")
@@ -6242,6 +6231,16 @@ func HandlerWithOptions(si ServerInterface, options GorillaServerOptions) http.H
 
 	r.HandleFunc(options.BaseURL+"/api/v2alpha1/routers/{id}/links", wrapper.LinksByRouter).Methods("GET")
 
+	r.HandleFunc(options.BaseURL+"/api/v2alpha1/services", wrapper.Services).Methods("GET")
+
+	r.HandleFunc(options.BaseURL+"/api/v2alpha1/services/{id}", wrapper.ServiceByID).Methods("GET")
+
+	r.HandleFunc(options.BaseURL+"/api/v2alpha1/services/{id}/connections", wrapper.ConnectionsByService).Methods("GET")
+
+	r.HandleFunc(options.BaseURL+"/api/v2alpha1/services/{id}/processes", wrapper.ProcessesByService).Methods("GET")
+
+	r.HandleFunc(options.BaseURL+"/api/v2alpha1/services/{id}/processpairs", wrapper.ProcessPairsByService).Methods("GET")
+
 	r.HandleFunc(options.BaseURL+"/api/v2alpha1/sitepairs", wrapper.Sitepairs).Methods("GET")
 
 	r.HandleFunc(options.BaseURL+"/api/v2alpha1/sitepairs/{id}", wrapper.SitepairByID).Methods("GET")
diff --git a/cmd/network-observer/internal/server/handlers.go b/cmd/network-observer/internal/server/handlers.go
index a1f7d696..de65efb4 100644
--- a/cmd/network-observer/internal/server/handlers.go
+++ b/cmd/network-observer/internal/server/handlers.go
@@ -20,8 +20,8 @@ func (s *server) Connections(w http.ResponseWriter, r *http.Request) {
 	}
 }
 
-// (GET /api/v2alpha1/addresses/{id}/connections)
-func (s *server) ConnectionsByAddress(w http.ResponseWriter, r *http.Request, id string) {
+// (GET /api/v2alpha1/services/{id}/connections)
+func (s *server) ConnectionsByService(w http.ResponseWriter, r *http.Request, id string) {
 	getExemplar := fetchAndMap(s.records, func(a collector.AddressRecord) store.Entry {
 		return store.Entry{Record: collector.ConnectionRecord{RoutingKey: a.Name, Protocol: a.Protocol}}
 	}, id)
@@ -39,24 +39,24 @@ func (s *server) Applicationflows(w http.ResponseWriter, r *http.Request) {
 	}
 }
 
-// (GET /api/v2alpha1/addresses)
-func (s *server) Addresses(w http.ResponseWriter, r *http.Request) {
-	results := views.NewAddressSliceProvider(s.records, s.graph)(listByType[collector.AddressRecord](s.records))
-	if err := handleCollection(w, r, &api.AddressListResponse{}, results); err != nil {
+// (GET /api/v2alpha1/services)
+func (s *server) Services(w http.ResponseWriter, r *http.Request) {
+	results := views.NewServiceSliceProvider(s.records, s.graph)(listByType[collector.AddressRecord](s.records))
+	if err := handleCollection(w, r, &api.ServiceListResponse{}, results); err != nil {
 		s.logWriteError(r, err)
 	}
 }
 
-// (GET /api/v2alpha1/addresses/{id})
-func (s *server) AddressByID(w http.ResponseWriter, r *http.Request, id string) {
-	getRecord := fetchAndMap(s.records, views.NewAddressProvider(s.records, s.graph), id)
-	if err := handleSingle(w, r, &api.AddressResponse{}, getRecord); err != nil {
+// (GET /api/v2alpha1/services/{id})
+func (s *server) ServiceByID(w http.ResponseWriter, r *http.Request, id string) {
+	getRecord := fetchAndMap(s.records, views.NewServiceProvider(s.records, s.graph), id)
+	if err := handleSingle(w, r, &api.ServiceResponse{}, getRecord); err != nil {
 		s.logWriteError(r, err)
 	}
 }
 
-// (GET /api/v2alpha1/addresses/{id}/processes)
-func (s *server) ProcessesByAddress(w http.ResponseWriter, r *http.Request, id string) {
+// (GET /api/v2alpha1/services/{id}/processes)
+func (s *server) ProcessesByService(w http.ResponseWriter, r *http.Request, id string) {
 	//todo(ck) find a way to more directly index this
 	anode := s.graph.Address(id)
 	if !anode.IsKnown() {
@@ -82,8 +82,8 @@ func (s *server) ProcessesByAddress(w http.ResponseWriter, r *http.Request, id s
 
 }
 
-// (GET /api/v2alpha1/addresses/{id}/processpairs)
-func (s *server) ProcessPairsByAddress(w http.ResponseWriter, r *http.Request, id string) {
+// (GET /api/v2alpha1/services/{id}/processes)
+func (s *server) ProcessPairsByService(w http.ResponseWriter, r *http.Request, id string) {
 	//todo(ck) find a way to more directly index this
 	addr, ok := s.graph.Address(id).GetRecord()
 	if !ok {
diff --git a/cmd/network-observer/internal/server/views/views.go b/cmd/network-observer/internal/server/views/views.go
index b364a554..16f5e7e4 100644
--- a/cmd/network-observer/internal/server/views/views.go
+++ b/cmd/network-observer/internal/server/views/views.go
@@ -144,11 +144,11 @@ func NewListenerProvider(graph collector.Graph) func(vanflow.ListenerRecord) api
 		setOpt(&out.Protocol, record.Protocol)
 		setOpt(&out.DestHost, record.DestHost)
 		setOpt(&out.DestPort, record.DestPort)
-		setOpt(&out.Address, record.Address)
+		setOpt(&out.RoutingKey, record.Address)
 
 		node := graph.Listener(record.ID)
 		if addressID := node.Address().ID(); addressID != "" {
-			out.AddressId = &addressID
+			out.ServiceId = &addressID
 		}
 		if site, ok := node.Parent().Parent().GetRecord(); ok {
 			out.SiteId = site.ID
@@ -160,15 +160,15 @@ func NewListenerProvider(graph collector.Graph) func(vanflow.ListenerRecord) api
 
 func defaultListener(id string) api.ListenerRecord {
 	return api.ListenerRecord{
-		Identity: id,
-		Name:     unknownStr,
-		Parent:   unknownStr,
-		Protocol: unknownStr,
-		Address:  unknownStr,
-		DestHost: unknownStr,
-		DestPort: unknownStr,
-		SiteId:   unknownStr,
-		SiteName: unknownStr,
+		Identity:   id,
+		Name:       unknownStr,
+		Parent:     unknownStr,
+		Protocol:   unknownStr,
+		RoutingKey: unknownStr,
+		DestHost:   unknownStr,
+		DestPort:   unknownStr,
+		SiteId:     unknownStr,
+		SiteName:   unknownStr,
 	}
 }
 
@@ -197,11 +197,11 @@ func NewConnectorProvider(graph collector.Graph) func(vanflow.ConnectorRecord) a
 		setOpt(&out.DestHost, record.DestHost)
 		setOpt(&out.DestPort, record.DestPort)
 		setOpt(&out.ProcessId, record.ProcessID)
-		setOpt(&out.Address, record.Address)
+		setOpt(&out.RoutingKey, record.Address)
 
 		node := graph.Connector(record.ID)
 		if addressID := node.Address().ID(); addressID != "" {
-			out.AddressId = &addressID
+			out.ServiceId = &addressID
 		}
 		if proc, ok := node.Target().GetRecord(); ok {
 			out.Target = proc.Name
@@ -217,15 +217,15 @@ func NewConnectorProvider(graph collector.Graph) func(vanflow.ConnectorRecord) a
 
 func defaultConnector(id string) api.ConnectorRecord {
 	return api.ConnectorRecord{
-		Identity: id,
-		Name:     unknownStr,
-		Parent:   unknownStr,
-		Protocol: unknownStr,
-		Address:  unknownStr,
-		DestHost: unknownStr,
-		DestPort: unknownStr,
-		SiteId:   unknownStr,
-		SiteName: unknownStr,
+		Identity:   id,
+		Name:       unknownStr,
+		Parent:     unknownStr,
+		Protocol:   unknownStr,
+		RoutingKey: unknownStr,
+		DestHost:   unknownStr,
+		DestPort:   unknownStr,
+		SiteId:     unknownStr,
+		SiteName:   unknownStr,
 	}
 }
 
@@ -359,7 +359,7 @@ func NewProcessProvider(stor store.Interface, graph collector.Graph) func(vanflo
 				addressList = append(addressList, addr)
 			}
 			sort.Slice(addressList, func(i, j int) bool { return addressList[i] < addressList[j] })
-			out.Addresses = &addressList
+			out.Services = &addressList
 		}
 
 		return out, true
@@ -508,10 +508,10 @@ func defaultRouterLink(id string) api.RouterLinkRecord {
 	}
 }
 
-func NewAddressSliceProvider(stor store.Interface, graph collector.Graph) func(entries []store.Entry) []api.AddressRecord {
-	provider := NewAddressProvider(stor, graph)
-	return func(entries []store.Entry) []api.AddressRecord {
-		results := make([]api.AddressRecord, 0, len(entries))
+func NewServiceSliceProvider(stor store.Interface, graph collector.Graph) func(entries []store.Entry) []api.ServiceRecord {
+	provider := NewServiceProvider(stor, graph)
+	return func(entries []store.Entry) []api.ServiceRecord {
+		results := make([]api.ServiceRecord, 0, len(entries))
 		for _, e := range entries {
 			record, ok := e.Record.(collector.AddressRecord)
 			if !ok {
@@ -523,7 +523,7 @@ func NewAddressSliceProvider(stor store.Interface, graph collector.Graph) func(e
 	}
 }
 
-func NewAddressProvider(stor store.Interface, graph collector.Graph) func(collector.AddressRecord) api.AddressRecord {
+func NewServiceProvider(stor store.Interface, graph collector.Graph) func(collector.AddressRecord) api.ServiceRecord {
 	requestRecordType := collector.RequestRecord{}.GetTypeMeta().String()
 	flowIndex := stor.IndexValues(collector.IndexFlowByAddress)
 	addressAppProtocols := make(map[string]map[string]struct{})
@@ -542,7 +542,7 @@ func NewAddressProvider(stor store.Interface, graph collector.Graph) func(collec
 		}
 		addressAppProtocols[address][protocol] = struct{}{}
 	}
-	return func(record collector.AddressRecord) api.AddressRecord {
+	return func(record collector.AddressRecord) api.ServiceRecord {
 		node := graph.Address(record.ID).RoutingKey()
 		listenerCt := len(node.Listeners())
 		connectorCt := len(node.Connectors())
@@ -551,7 +551,7 @@ func NewAddressProvider(stor store.Interface, graph collector.Graph) func(collec
 		for proto := range addressAppProtocols[record.Name] {
 			protocols = append(protocols, proto)
 		}
-		return api.AddressRecord{
+		return api.ServiceRecord{
 			Identity:                     record.ID,
 			StartTime:                    uint64(record.Start.UnixMicro()),
 			Protocol:                     record.Protocol,
@@ -690,8 +690,8 @@ func NewSiteProvider(graph collector.Graph) func(vanflow.SiteRecord) api.SiteRec
 				s.Platform = api.SitePlatformTypeDocker
 			case strings.EqualFold(platform, string(api.SitePlatformTypePodman)):
 				s.Platform = api.SitePlatformTypePodman
-			case strings.EqualFold(platform, string(api.SitePlatformTypeSystemd)):
-				s.Platform = api.SitePlatformTypeSystemd
+			case strings.EqualFold(platform, string(api.SitePlatformTypeLinux)):
+				s.Platform = api.SitePlatformTypeLinux
 			}
 		}
 		s.RouterCount = len(graph.Site(site.ID).Routers())
diff --git a/cmd/network-observer/spec/openapi.yaml b/cmd/network-observer/spec/openapi.yaml
index f3046a7d..2b985b23 100644
--- a/cmd/network-observer/spec/openapi.yaml
+++ b/cmd/network-observer/spec/openapi.yaml
@@ -113,24 +113,24 @@ paths:
           $ref: '#/components/responses/getConnectorByID'
         '404':
           $ref: '#/components/responses/errorNotFound'
-  /api/v2alpha1/addresses:
+  /api/v2alpha1/services:
     get:
-      tags: [address]
-      operationId: addresses
+      tags: [service]
+      operationId: services
       responses:
         '200':
-          $ref: '#/components/responses/getAddresses'
+          $ref: '#/components/responses/getServices'
         '400':
           $ref: '#/components/responses/errorBadRequest'
-  /api/v2alpha1/addresses/{id}:
+  /api/v2alpha1/services/{id}:
     get:
-      tags: [address]
-      operationId: addressByID
+      tags: [service]
+      operationId: serviceByID
       parameters:
         - $ref: '#/components/parameters/pathID'
       responses:
         '200':
-          $ref: '#/components/responses/getAddressByID'
+          $ref: '#/components/responses/getServiceByID'
         '404':
           $ref: '#/components/responses/errorNotFound'
   /api/v2alpha1/processgroups:
@@ -362,10 +362,10 @@ paths:
       responses:
         '410':
           $ref: '#/components/responses/notSupported'
-  /api/v2alpha1/addresses/{id}/processes:
+  /api/v2alpha1/services/{id}/processes:
     get:
-      tags: [address, process]
-      operationId: processesByAddress
+      tags: [service, process]
+      operationId: processesByService
       parameters:
         - $ref: '#/components/parameters/pathID'
       responses:
@@ -373,10 +373,10 @@ paths:
           $ref: '#/components/responses/getProcesses'
         '400':
           $ref: '#/components/responses/errorBadRequest'
-  /api/v2alpha1/addresses/{id}/processpairs:
+  /api/v2alpha1/services/{id}/processpairs:
     get:
-      tags: [address, "flow aggregate"]
-      operationId: processPairsByAddress
+      tags: [service, "flow aggregate"]
+      operationId: processPairsByService
       parameters:
         - $ref: '#/components/parameters/pathID'
       responses:
@@ -384,10 +384,10 @@ paths:
           $ref: '#/components/responses/getFlowAggregates'
         '404':
           $ref: '#/components/responses/errorNotFound'
-  /api/v2alpha1/addresses/{id}/connections:
+  /api/v2alpha1/services/{id}/connections:
     get:
-      tags: [address, flows]
-      operationId: connectionsByAddress
+      tags: [service, flows]
+      operationId: connectionsByService
       parameters:
         - $ref: '#/components/parameters/pathID'
       responses:
@@ -463,12 +463,12 @@ components:
         application/json:
           schema:
             $ref: '#/components/schemas/ConnectorListResponse'
-    getAddresses:
-      description: response with a list of addresses
+    getServices:
+      description: response with a list of services
       content:
         application/json:
           schema:
-            $ref: '#/components/schemas/AddressListResponse'
+            $ref: '#/components/schemas/ServiceListResponse'
     getProcessGroups:
       description: response with a list of process groups
       content:
@@ -541,12 +541,12 @@ components:
         application/json:
           schema:
             $ref: '#/components/schemas/ConnectorResponse'
-    getAddressByID:
-      description: response with a single address
+    getServiceByID:
+      description: response with a single service
       content:
         application/json:
           schema:
-            $ref: '#/components/schemas/AddressResponse'
+            $ref: '#/components/schemas/ServiceResponse'
     getProcessGroupByID:
       description: response with a single process group
       content:
@@ -688,7 +688,7 @@ components:
         properties:
           results:
             $ref: '#/components/schemas/RouterRecord'
-    AddressListResponse:
+    ServiceListResponse:
       allOf:
         - $ref: '#/components/schemas/collectionResponse'
         - type: object
@@ -697,13 +697,13 @@ components:
             results:
               type: array
               items:
-                $ref: '#/components/schemas/AddressRecord'
-    AddressResponse:
+                $ref: '#/components/schemas/ServiceRecord'
+    ServiceResponse:
         type: object
         required: [results]
         properties:
           results:
-            $ref: '#/components/schemas/AddressRecord'
+            $ref: '#/components/schemas/ServiceRecord'
     ProcessGroupListResponse:
       allOf:
         - $ref: '#/components/schemas/collectionResponse'
@@ -835,7 +835,7 @@ components:
         - kubernetes
         - podman
         - docker
-        - systemd
+        - linux
         - unknown
     processBindingType:
       type: string
@@ -843,9 +843,9 @@ components:
       enum:
         - bound
         - unbound
-    addressIdentifierType:
+    serviceIdentifierType:
       type: string
-      description: a special string for identifying addresses uses the form `name@identity@protocol`
+      description: a special string for identifying services uses the form `name@identity@protocol`
       example: backend@11111-1234@tcp
       x-go-type: AtmarkDelimitedString
     linkRoleType:
@@ -898,7 +898,7 @@ components:
               - hostName
               - processRole
               - processBinding
-              - addresses
+              - services
           properties:
             name:
               type: string
@@ -931,11 +931,11 @@ components:
               description: Internal processes are processes related to Skupper. Remote processes are processes indirectly connected, such as a proxy
             processBinding:
               $ref: '#/components/schemas/processBindingType'
-            addresses:
+            services:
               type: array
               nullable: true
               items:
-                $ref: '#/components/schemas/addressIdentifierType'
+                $ref: '#/components/schemas/serviceIdentifierType'
     RouterRecord:
       allOf:
         - $ref: '#/components/schemas/baseRecord'
@@ -975,7 +975,7 @@ components:
             - destHost
             - destPort
             - protocol
-            - address
+            - routingKey
             - siteId
             - siteName
           properties:
@@ -989,9 +989,9 @@ components:
               type: string
             protocol:
               type: string
-            address:
+            routingKey:
               type: string
-            addressId:
+            serviceId:
               type: string
             siteId:
               type: string
@@ -1007,7 +1007,7 @@ components:
             - destHost
             - destPort
             - protocol
-            - address
+            - routingKey
             - processId
             - target
             - siteId
@@ -1023,9 +1023,9 @@ components:
               type: string
             protocol:
               type: string
-            address:
+            routingKey:
               type: string
-            addressId:
+            serviceId:
               type: string
             processId:
               type: string
@@ -1036,7 +1036,7 @@ components:
               type: string
             siteName:
               type: string
-    AddressRecord:
+    ServiceRecord:
       allOf:
         - $ref: '#/components/schemas/baseRecord'
         - type: object
@@ -1067,7 +1067,7 @@ components:
               description: true when there are both listeners and connectors configured
             hasListener:
               type: boolean
-              description: true when there is at least one listener for this address
+              description: true when there is at least one listener for this routingKey
     ProcessGroupRecord:
       allOf:
         - $ref: '#/components/schemas/baseRecord'
@@ -1442,8 +1442,8 @@ tags:
     description: requests involving Listener records
   - name: connector
     description: requests involving Connector records
-  - name: address
-    description: requests involving Address records
+  - name: service
+    description: requests involving Service records
   - name: process group
     description: requests involving ProcessGroup records
   - name: flow aggregate
diff --git a/internal/cmd/skupper/common/command.go b/internal/cmd/skupper/common/command.go
index 1b37f1d0..feea41b1 100644
--- a/internal/cmd/skupper/common/command.go
+++ b/internal/cmd/skupper/common/command.go
@@ -2,6 +2,7 @@ package common
 
 import (
 	"fmt"
+
 	"github.com/skupperproject/skupper/api/types"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"github.com/spf13/cobra"
@@ -41,7 +42,7 @@ func ConfigureCobraCommand(configuredPlatform types.Platform, description Skuppe
 			switch platform {
 			case "kubernetes":
 				skupperCommand = kubeImpl
-			case "podman", "docker", "systemd":
+			case "podman", "docker", "linux":
 				skupperCommand = nonKubeImpl
 			default:
 				return fmt.Errorf("platform %q not supported", platform)
diff --git a/internal/cmd/skupper/common/flags.go b/internal/cmd/skupper/common/flags.go
index f7520297..13cc0eaa 100644
--- a/internal/cmd/skupper/common/flags.go
+++ b/internal/cmd/skupper/common/flags.go
@@ -6,7 +6,7 @@ import (
 
 const (
 	FlagNamePlatform   = "platform"
-	FlagDescPlatform   = "Set the platform type to use [kubernetes, podman, docker, systemd]"
+	FlagDescPlatform   = "Set the platform type to use [kubernetes, podman, docker, linux]"
 	FlagNameNamespace  = "namespace"
 	FlagDescNamespace  = "Set the namespace"
 	FlagNameContext    = "context"
diff --git a/internal/cmd/skupper/system/nonkube/system_reload.go b/internal/cmd/skupper/system/nonkube/system_reload.go
index 9a2a731e..250d3c45 100644
--- a/internal/cmd/skupper/system/nonkube/system_reload.go
+++ b/internal/cmd/skupper/system/nonkube/system_reload.go
@@ -58,7 +58,7 @@ func (cmd *CmdSystemReload) InputToOptions() {
 	}
 
 	switch selectedPlatform {
-	case types.PlatformSystemd:
+	case types.PlatformLinux:
 		binary = "skrouterd"
 	case types.PlatformDocker:
 		binary = "docker"
diff --git a/internal/cmd/skupper/system/nonkube/system_setup.go b/internal/cmd/skupper/system/nonkube/system_setup.go
index 1194ab35..17e2c1a1 100644
--- a/internal/cmd/skupper/system/nonkube/system_setup.go
+++ b/internal/cmd/skupper/system/nonkube/system_setup.go
@@ -104,7 +104,7 @@ func (cmd *CmdSystemSetup) InputToOptions() {
 
 	if !isBundle {
 		switch selectedPlatform {
-		case types.PlatformSystemd:
+		case types.PlatformLinux:
 			binary = "skrouterd"
 		case types.PlatformDocker:
 			binary = "docker"
diff --git a/internal/config/platform.go b/internal/config/platform.go
index bfd1e0e6..5417a680 100644
--- a/internal/config/platform.go
+++ b/internal/config/platform.go
@@ -44,8 +44,8 @@ func GetPlatform() types.Platform {
 		return types.PlatformPodman
 	case types.PlatformDocker:
 		return types.PlatformDocker
-	case types.PlatformSystemd:
-		return types.PlatformSystemd
+	case types.PlatformLinux:
+		return types.PlatformLinux
 	default:
 		return types.PlatformKubernetes
 	}
diff --git a/internal/kube/client/fake/fake_client.go b/internal/kube/client/fake/fake_client.go
index 72a1fbe0..66e3f9ae 100644
--- a/internal/kube/client/fake/fake_client.go
+++ b/internal/kube/client/fake/fake_client.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 
 	routev1 "github.com/openshift/api/route/v1"
+	appsv1 "k8s.io/api/apps/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/runtime"
@@ -39,7 +40,7 @@ func NewFakeClient(namespace string, k8sObjects []runtime.Object, skupperObjects
 	}
 
 	c.Namespace = namespace
-	c.Kube = k8sfake.NewSimpleClientset(standard...)
+	c.Kube = k8sfake.NewClientset(standard...)
 	c.Skupper = skupperclientfake.NewSimpleClientset(skupperObjects...)
 	// Note: brute force error return for any client access, we could make it more granular if needed
 	if fakeSkupperError != "" {
@@ -47,10 +48,13 @@ func NewFakeClient(namespace string, k8sObjects []runtime.Object, skupperObjects
 			return true, nil, fmt.Errorf(fakeSkupperError)
 		})
 	}
-	c.Dynamic = dynamicfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), map[schema.GroupVersionResource]string{
+	scheme := runtime.NewScheme()
+	appsv1.AddToScheme(scheme)
+	c.Dynamic = dynamicfake.NewSimpleDynamicClientWithCustomListKinds(scheme, map[schema.GroupVersionResource]string{
 		resource.ContourHttpProxyResource(): "HTTPProxyList",
 		resource.GatewayResource():          "GatewayList",
 		resource.TlsRouteResource():         "TLSRouteList",
+		resource.DeploymentResource():       "DeploymentList",
 	}, dynamic...)
 	// prepopulated objects not working for some reason with dynamic client, so create them manually here for now:
 	for _, d := range dynamic {
diff --git a/internal/kube/controller/config.go b/internal/kube/controller/config.go
new file mode 100644
index 00000000..7cc2c16e
--- /dev/null
+++ b/internal/kube/controller/config.go
@@ -0,0 +1,40 @@
+package controller
+
+import (
+	"flag"
+
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	iflag "github.com/skupperproject/skupper/internal/flag"
+	"github.com/skupperproject/skupper/internal/kube/grants"
+	"github.com/skupperproject/skupper/internal/kube/securedaccess"
+)
+
+type Config struct {
+	GrantConfig         *grants.GrantConfig
+	SecuredAccessConfig *securedaccess.Config
+	Namespace           string
+	Kubeconfig          string
+	WatchNamespace      string
+}
+
+func BoundConfig(flags *flag.FlagSet) (*Config, error) {
+	grantConfig, err := grants.BoundGrantConfig(flags)
+	if err != nil {
+		return nil, err
+	}
+	securedAccessConfig, err := securedaccess.BoundConfig(flags)
+	if err != nil {
+		return nil, err
+	} else if err := securedAccessConfig.Verify(); err != nil {
+		return nil, err
+	}
+	c := &Config{
+		GrantConfig:         grantConfig,
+		SecuredAccessConfig: securedAccessConfig,
+	}
+	iflag.StringVar(flags, &c.Namespace, "namespace", "NAMESPACE", "", "The Kubernetes namespace scope for the controller")
+	iflag.StringVar(flags, &c.Kubeconfig, "kubeconfig", "KUBECONFIG", "", "A path to the kubeconfig file to use")
+	iflag.StringVar(flags, &c.WatchNamespace, "watch-namespace", "WATCH_NAMESPACE", metav1.NamespaceAll, "The Kubernetes namespace the controller should monitor for controlled resources (will monitor all if not specified)")
+	return c, nil
+}
diff --git a/internal/kube/controller/controller.go b/internal/kube/controller/controller.go
index 95bc6983..a88f574b 100644
--- a/internal/kube/controller/controller.go
+++ b/internal/kube/controller/controller.go
@@ -38,19 +38,13 @@ type Controller struct {
 	attachableConnectors map[string]*skupperv2alpha1.AttachedConnector
 }
 
-func skupperRouterService() internalinterfaces.TweakListOptionsFunc {
-	return func(options *metav1.ListOptions) {
-		options.FieldSelector = "metadata.name=skupper-router"
-	}
-}
-
 func skupperNetworkStatus() internalinterfaces.TweakListOptionsFunc {
 	return func(options *metav1.ListOptions) {
 		options.FieldSelector = "metadata.name=skupper-network-status"
 	}
 }
 
-func NewController(cli internalclient.Clients, grantConfig *grants.GrantConfig, securedAccessConfig *securedaccess.Config, watchNamespace string, currentNamespace string) (*Controller, error) {
+func NewController(cli internalclient.Clients, config *Config) (*Controller, error) {
 	controller := &Controller{
 		controller:           internalclient.NewController("Controller", cli),
 		sites:                map[string]*site.Site{},
@@ -58,9 +52,9 @@ func NewController(cli internalclient.Clients, grantConfig *grants.GrantConfig,
 	}
 
 	podname := os.Getenv("HOSTNAME")
-	owner, err := controller.controller.GetDeploymentForPod(podname, currentNamespace)
+	owner, err := controller.controller.GetDeploymentForPod(podname, config.Namespace)
 	controllerContext := securedaccess.ControllerContext{
-		Namespace: currentNamespace,
+		Namespace: config.Namespace,
 	}
 	if err != nil {
 		log.Printf("Could not deduce owning deployment, some resources may need to be manually deleted when controller is deleted: %s", err)
@@ -69,34 +63,41 @@ func NewController(cli internalclient.Clients, grantConfig *grants.GrantConfig,
 		controllerContext.UID = string(owner.UID)
 	}
 
-	controller.siteWatcher = controller.controller.WatchSites(watchNamespace, controller.checkSite)
-	controller.listenerWatcher = controller.controller.WatchListeners(watchNamespace, controller.checkListener)
-	controller.connectorWatcher = controller.controller.WatchConnectors(watchNamespace, controller.checkConnector)
-	controller.linkAccessWatcher = controller.controller.WatchRouterAccesses(watchNamespace, controller.checkRouterAccess)
-	controller.controller.WatchAttachedConnectors(watchNamespace, controller.checkAttachedConnector)
-	controller.controller.WatchAttachedConnectorBindings(watchNamespace, controller.checkAttachedConnectorBinding)
-	controller.controller.WatchLinks(watchNamespace, controller.checkLink)
-	controller.controller.WatchConfigMaps(skupperNetworkStatus(), watchNamespace, controller.networkStatusUpdate)
-	controller.controller.WatchAccessTokens(watchNamespace, controller.checkAccessToken)
-	controller.controller.WatchPods("skupper.io/component=router,skupper.io/type=site", watchNamespace, controller.routerPodEvent)
+	controller.siteWatcher = controller.controller.WatchSites(config.WatchNamespace, controller.checkSite)
+	controller.listenerWatcher = controller.controller.WatchListeners(config.WatchNamespace, controller.checkListener)
+	controller.connectorWatcher = controller.controller.WatchConnectors(config.WatchNamespace, controller.checkConnector)
+	controller.linkAccessWatcher = controller.controller.WatchRouterAccesses(config.WatchNamespace, controller.checkRouterAccess)
+	controller.controller.WatchAttachedConnectors(config.WatchNamespace, controller.checkAttachedConnector)
+	controller.controller.WatchAttachedConnectorBindings(config.WatchNamespace, controller.checkAttachedConnectorBinding)
+	controller.controller.WatchLinks(config.WatchNamespace, controller.checkLink)
+	controller.controller.WatchConfigMaps(skupperNetworkStatus(), config.WatchNamespace, controller.networkStatusUpdate)
+	controller.controller.WatchAccessTokens(config.WatchNamespace, controller.checkAccessToken)
+	controller.controller.WatchPods("skupper.io/component=router,skupper.io/type=site", config.WatchNamespace, controller.routerPodEvent)
 
 	controller.certMgr = certificates.NewCertificateManager(controller.controller)
-	controller.certMgr.Watch(watchNamespace)
+	controller.certMgr.Watch(config.WatchNamespace)
 
-	controller.accessMgr = securedaccess.NewSecuredAccessManager(controller.controller, controller.certMgr, securedAccessConfig, controllerContext)
+	controller.accessMgr = securedaccess.NewSecuredAccessManager(controller.controller, controller.certMgr, config.SecuredAccessConfig, controllerContext)
 	controller.accessRecovery = securedaccess.NewSecuredAccessResourceWatcher(controller.accessMgr)
-	controller.accessRecovery.WatchResources(controller.controller, watchNamespace)
-	controller.accessRecovery.WatchSecuredAccesses(controller.controller, watchNamespace, controller.checkSecuredAccess)
-	controller.accessRecovery.WatchGateway(controller.controller, currentNamespace)
+	controller.accessRecovery.WatchResources(controller.controller, config.WatchNamespace)
+	controller.accessRecovery.WatchSecuredAccesses(controller.controller, config.WatchNamespace, controller.checkSecuredAccess)
+	controller.accessRecovery.WatchGateway(controller.controller, config.Namespace)
 
-	controller.startGrantServer = grants.Initialise(controller.controller, currentNamespace, watchNamespace, grantConfig, controller.generateLinkConfig)
+	controller.startGrantServer = grants.Initialise(controller.controller, config.Namespace, config.WatchNamespace, config.GrantConfig, controller.generateLinkConfig)
 
-	controller.controller.WatchConfigMaps(skupperLogConfig(), currentNamespace, controller.logConfigUpdate)
+	controller.controller.WatchConfigMaps(skupperLogConfig(), config.Namespace, controller.logConfigUpdate)
 
 	return controller, nil
 }
 
 func (c *Controller) Run(stopCh <-chan struct{}) error {
+	if err := c.init(stopCh); err != nil {
+		return err
+	}
+	return c.start(stopCh)
+}
+
+func (c *Controller) init(stopCh <-chan struct{}) error {
 	log.Println("Starting informers")
 	c.controller.StartWatchers(stopCh)
 	c.stopCh = stopCh
@@ -140,7 +141,10 @@ func (c *Controller) Run(stopCh <-chan struct{}) error {
 	if c.startGrantServer != nil {
 		c.startGrantServer()
 	}
+	return nil
+}
 
+func (c *Controller) start(stopCh <-chan struct{}) error {
 	log.Println("Starting event loop")
 	c.controller.Start(stopCh)
 	<-stopCh
diff --git a/internal/kube/qdr/ssl_profile.go b/internal/kube/qdr/ssl_profile.go
deleted file mode 100644
index 0fddcc51..00000000
--- a/internal/kube/qdr/ssl_profile.go
+++ /dev/null
@@ -1,103 +0,0 @@
-package qdr
-
-import (
-	"context"
-
-	"github.com/skupperproject/skupper/api/types"
-	"github.com/skupperproject/skupper/pkg/qdr"
-
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/client-go/kubernetes"
-)
-
-func AddSslProfile(sslProfile qdr.SslProfile, namespace string, cli kubernetes.Interface) error {
-
-	configmap, err := getConfigMap(types.TransportConfigMapName, namespace, cli)
-	if err != nil {
-		return err
-	}
-	current, err := qdr.GetRouterConfigFromConfigMap(configmap)
-	if err != nil {
-		return err
-	}
-
-	if current != nil {
-		if _, ok := current.SslProfiles[sslProfile.Name]; !ok {
-			current.AddSslProfile(qdr.SslProfile{
-				Name:       sslProfile.Name,
-				CertFile:   sslProfile.CertFile,
-				CaCertFile: sslProfile.CaCertFile,
-			})
-		}
-		_, err = current.UpdateConfigMap(configmap)
-		if err != nil {
-			return err
-		}
-
-		_, err = cli.CoreV1().ConfigMaps(namespace).Update(context.TODO(), configmap, metav1.UpdateOptions{})
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-
-}
-
-func RemoveSslProfile(secretName string, namespace string, cli kubernetes.Interface) error {
-	configmap, err := getConfigMap(types.TransportConfigMapName, namespace, cli)
-	if err != nil {
-		return err
-	}
-	current, err := qdr.GetRouterConfigFromConfigMap(configmap)
-	if err != nil {
-		return err
-	}
-
-	if current != nil {
-		if _, ok := current.SslProfiles[secretName]; ok {
-			current.RemoveSslProfile(secretName)
-		}
-
-		_, err = current.UpdateConfigMap(configmap)
-		if err != nil {
-			return err
-		}
-
-		_, err = cli.CoreV1().ConfigMaps(namespace).Update(context.TODO(), configmap, metav1.UpdateOptions{})
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func ExistsSslProfile(secretName string, namespace string, cli kubernetes.Interface) (bool, error) {
-
-	configmap, err := getConfigMap(types.TransportConfigMapName, namespace, cli)
-	if err != nil {
-		return false, err
-	}
-	current, err := qdr.GetRouterConfigFromConfigMap(configmap)
-	if err != nil {
-		return false, err
-	}
-
-	if current != nil {
-		_, ok := current.SslProfiles[secretName]
-
-		return ok, nil
-	}
-
-	return false, nil
-
-}
-
-func getConfigMap(name string, namespace string, cli kubernetes.Interface) (*corev1.ConfigMap, error) {
-	current, err := cli.CoreV1().ConfigMaps(namespace).Get(context.TODO(), name, metav1.GetOptions{})
-	if err != nil {
-		return nil, err
-	} else {
-		return current, err
-	}
-}
diff --git a/internal/kube/resource/definitions.go b/internal/kube/resource/definitions.go
index cea6ffd7..22a77b17 100644
--- a/internal/kube/resource/definitions.go
+++ b/internal/kube/resource/definitions.go
@@ -29,6 +29,14 @@ func TlsRouteResource() schema.GroupVersionResource {
 	}
 }
 
+func DeploymentResource() schema.GroupVersionResource {
+	return schema.GroupVersionResource{
+		Group:    "apps",
+		Version:  "v1",
+		Resource: "deployments",
+	}
+}
+
 func IsResourceAvailable(client discovery.DiscoveryInterface, resource schema.GroupVersionResource) bool {
 	resources, err := client.ServerResourcesForGroupVersion(resource.GroupVersion().String())
 	if err != nil {
diff --git a/internal/kube/securedaccess/access.go b/internal/kube/securedaccess/access.go
index aea2202c..7635aaf3 100644
--- a/internal/kube/securedaccess/access.go
+++ b/internal/kube/securedaccess/access.go
@@ -324,6 +324,9 @@ func (m *SecuredAccessManager) CheckRoute(key string, route *routev1.Route) erro
 	} else {
 		m.routes[key] = route
 		if sa == nil {
+			if !canDelete(&route.ObjectMeta) {
+				return nil
+			}
 			log.Printf("Deleting route %s/%s as no matching ServiceAccess definition found", route.Namespace, route.Name)
 			return m.clients.GetRouteClient().Routes(route.Namespace).Delete(context.Background(), route.Name, metav1.DeleteOptions{})
 		}
@@ -375,6 +378,9 @@ func (m *SecuredAccessManager) CheckIngress(key string, ingress *networkingv1.In
 	} else {
 		m.ingresses[key] = ingress
 		if !ok || m.actualAccessType(sa) != ACCESS_TYPE_INGRESS_NGINX {
+			if !canDelete(&ingress.ObjectMeta) {
+				return nil
+			}
 			// delete this ingress as there is no corresponding securedaccess resource
 			log.Printf("Deleting redundant Ingress %s/%s", ingress.Namespace, ingress.Name)
 			return m.clients.GetKubeClient().NetworkingV1().Ingresses(ingress.Namespace).Delete(context.Background(), ingress.Name, metav1.DeleteOptions{})
@@ -402,7 +408,11 @@ func (m *SecuredAccessManager) CheckService(key string, svc *corev1.Service) err
 	}
 	sa, ok := m.definitions[key]
 	if !ok {
+		if !canDelete(&svc.ObjectMeta) {
+			return nil
+		}
 		// delete this service as there is no corresponding securedaccess resource
+		log.Printf("Deleting redundant service %s/%s", svc.Namespace, svc.Name)
 		return m.clients.GetKubeClient().CoreV1().Services(svc.Namespace).Delete(context.Background(), svc.Name, metav1.DeleteOptions{})
 	}
 	m.services[key] = svc
@@ -525,3 +535,23 @@ func getHosts(sa *skupperv2alpha1.SecuredAccess) []string {
 	}
 	return results
 }
+
+func canDelete(obj *metav1.ObjectMeta) bool {
+	return isOwned(obj) && hasSecuredAccessLabel(obj)
+}
+
+func isOwned(obj *metav1.ObjectMeta) bool {
+	if obj.Annotations == nil {
+		return false
+	}
+	_, ok := obj.Annotations["internal.skupper.io/controlled"]
+	return ok
+}
+
+func hasSecuredAccessLabel(obj *metav1.ObjectMeta) bool {
+	if obj.Labels == nil {
+		return false
+	}
+	_, ok := obj.Labels["internal.skupper.io/secured-access"]
+	return ok
+}
diff --git a/internal/kube/site/extended_bindings.go b/internal/kube/site/extended_bindings.go
index 9ec6438d..7c46ccea 100644
--- a/internal/kube/site/extended_bindings.go
+++ b/internal/kube/site/extended_bindings.go
@@ -92,8 +92,9 @@ func (a *ExtendedBindings) ListenerUpdated(listener *skupperv2alpha1.Listener) {
 	if err != nil {
 		bindings_logger.Error("Unable to get port for listener",
 			slog.String("namespace", listener.Namespace),
-			slog.String("name", listener.Name))
-		slog.Any("error", err)
+			slog.String("name", listener.Name),
+			slog.Any("error", err),
+		)
 	} else {
 		port := Port{
 			Name:       listener.Name,
@@ -104,6 +105,15 @@ func (a *ExtendedBindings) ListenerUpdated(listener *skupperv2alpha1.Listener) {
 		if exposed := a.exposed.Expose(listener.Spec.Host, port); exposed != nil {
 			if err := a.context.Expose(exposed); err != nil {
 				//TODO: write error to listener status
+				bindings_logger.Error("Error exposing listener",
+					slog.String("namespace", listener.Namespace),
+					slog.String("name", listener.Name),
+					slog.Any("error", err))
+			} else {
+				bindings_logger.Info("Exposed listener",
+					slog.String("namespace", listener.Namespace),
+					slog.String("name", listener.Name))
+
 			}
 		}
 	}
@@ -119,6 +129,14 @@ func (a *ExtendedBindings) ListenerDeleted(listener *skupperv2alpha1.Listener) {
 		} else {
 			if err := a.context.Expose(exposed); err != nil {
 				//TODO: write error to listener status
+				bindings_logger.Error("Error re-exposing service after deleting listener",
+					slog.String("namespace", listener.Namespace),
+					slog.String("name", listener.Name),
+					slog.Any("error", err))
+			} else {
+				bindings_logger.Info("Re-exposed service after deleting listener",
+					slog.String("namespace", listener.Namespace),
+					slog.String("name", listener.Name))
 			}
 		}
 	}
@@ -182,7 +200,7 @@ func (b *ExtendedBindings) UpdateListener(name string, listener *skupperv2alpha1
 				updateConfig = true
 			}
 		} else {
-			b.perTargetListeners[name] = newPerTargetListener(listener)
+			b.perTargetListeners[name] = newPerTargetListener(listener, b.logger)
 		}
 	} else {
 		if existing, ok := b.perTargetListeners[name]; ok {
diff --git a/internal/kube/site/per_target_listener.go b/internal/kube/site/per_target_listener.go
index 56841aba..c81bd500 100644
--- a/internal/kube/site/per_target_listener.go
+++ b/internal/kube/site/per_target_listener.go
@@ -1,6 +1,7 @@
 package site
 
 import (
+	"log/slog"
 	"reflect"
 	"strconv"
 	"strings"
@@ -12,12 +13,14 @@ import (
 type PerTargetListener struct {
 	definition *skupperv2alpha1.Listener
 	targets    map[string]int // name -> port
+	logger     *slog.Logger
 }
 
-func newPerTargetListener(l *skupperv2alpha1.Listener) *PerTargetListener {
+func newPerTargetListener(l *skupperv2alpha1.Listener, logger *slog.Logger) *PerTargetListener {
 	return &PerTargetListener{
 		definition: l,
 		targets:    map[string]int{},
+		logger:     logger,
 	}
 }
 
@@ -28,6 +31,9 @@ func (p *PerTargetListener) updateListener(l *skupperv2alpha1.Listener) bool {
 }
 
 func (p *PerTargetListener) extractTargets(network []skupperv2alpha1.SiteRecord, mapping *qdr.PortMapping, exposedPorts ExposedPorts, context BindingContext) (bool, error) {
+	p.logger.Info("Extracting targets for listener",
+		slog.String("namespace", p.definition.Namespace),
+		slog.String("listener", p.definition.Name))
 	targets := extractTargets(p.address(""), network)
 	changed := false
 	stale := map[string]bool{}
@@ -52,7 +58,13 @@ func (p *PerTargetListener) extractTargets(network []skupperv2alpha1.SiteRecord,
 		}
 		delete(p.targets, target)
 	}
-	return changed, nil
+	if !changed {
+		return false, nil
+	}
+	if err := p.expose(mapping, exposedPorts, context); err != nil {
+		return true, err
+	}
+	return true, nil
 }
 
 func (p *PerTargetListener) address(target string) string {
@@ -69,8 +81,20 @@ func (p *PerTargetListener) expose(mapping *qdr.PortMapping, exposedPorts Expose
 		}
 		if ports := exposedPorts.Expose(target, port); ports != nil {
 			if err := context.Expose(ports); err != nil {
+				p.logger.Error("Error exposing per target listener",
+					slog.String("namespace", p.definition.Namespace),
+					slog.String("listener", p.definition.Name),
+					slog.String("target", target),
+					slog.Any("error", err),
+				)
+
 				return err
 			}
+			p.logger.Info("Exposed per target listener",
+				slog.String("namespace", p.definition.Namespace),
+				slog.String("listener", p.definition.Name),
+				slog.String("target", target),
+			)
 		}
 	}
 	return nil
@@ -107,7 +131,6 @@ func (p *PerTargetListener) updateBridgeConfig(siteId string, config *qdr.Bridge
 			config.AddTcpListener(qdr.TcpEndpoint{
 				Name:       p.definition.Name + "@" + target,
 				SiteId:     siteId,
-				Host:       "0.0.0.0",
 				Port:       strconv.Itoa(port),
 				Address:    p.address(target),
 				SslProfile: p.definition.Spec.TlsCredentials,
@@ -127,18 +150,3 @@ func extractTargets(prefix string, network []skupperv2alpha1.SiteRecord) []strin
 	}
 	return results
 }
-
-func equivalentSlices(a []string, b []string) bool {
-	if len(a) != len(b) {
-		return false
-	}
-	for _, s := range a {
-		for _, r := range b {
-			if s == r {
-				continue
-			}
-		}
-		return false
-	}
-	return true
-}
diff --git a/internal/kube/site/site.go b/internal/kube/site/site.go
index e890040c..c4e9bfff 100644
--- a/internal/kube/site/site.go
+++ b/internal/kube/site/site.go
@@ -560,6 +560,10 @@ func (s *Site) Unexpose(name string) error {
 				slog.String("namespace", s.namespace),
 				slog.Any("error", err))
 			return err
+		} else {
+			s.logger.Info("Deleted service",
+				slog.String("service", name),
+				slog.String("namespace", s.namespace))
 		}
 	}
 	return nil
diff --git a/internal/nonkube/bundle/install.sh.template b/internal/nonkube/bundle/install.sh.template
index 6f495fe1..164f5f1a 100644
--- a/internal/nonkube/bundle/install.sh.template
+++ b/internal/nonkube/bundle/install.sh.template
@@ -49,8 +49,8 @@ exit_error() {
 }
 
 usage() {
-    echo "Usage: $0 [-p <podman|docker|systemd>] [-x] [-d <output-dir>]" >&2
-    echo "    -p    the platform to use: podman, docker, systemd (default: ${SOURCE_PLATFORM})" >&2
+    echo "Usage: $0 [-p <podman|docker|linux>] [-x] [-d <output-dir>]" >&2
+    echo "    -p    the platform to use: podman, docker, linux (default: ${SOURCE_PLATFORM})" >&2
     echo "    -n    target namespace (default: ${SOURCE_NAMESPACE})" >&2
     echo "    -x    remove existing site definition" >&2
     echo "    -d    dump static links from bundle into the provide output directory" >&2
@@ -61,7 +61,7 @@ parse_opts() {
     while getopts "xhd:p:n:" opt; do
         case "${opt}" in
             p)
-                valid_platforms="podman docker systemd"
+                valid_platforms="podman docker linux"
                 export SKUPPER_PLATFORM="${OPTARG}"
                 is_platform_valid=false
                 for valid_platform in ${valid_platforms}; do
@@ -70,7 +70,7 @@ parse_opts() {
                         break
                     fi
                 done
-                if [ ! "${is_platform_valid}" ]; then
+                if [ "${is_platform_valid}" = "false" ]; then
                     echo "Invalid platform: ${SKUPPER_PLATFORM}"
                     usage
                 fi
@@ -113,7 +113,7 @@ parse_opts() {
         fi
         docker_gid=$(getent group docker | cut -d: -f3)
         export RUNAS="${UID}:${docker_gid}"
-    elif [ "${SKUPPER_PLATFORM}" = "systemd" ]; then
+    elif [ "${SKUPPER_PLATFORM}" = "linux" ]; then
         export PLATFORM_COMMAND="skrouterd"
     fi
 
@@ -124,7 +124,7 @@ create_service() {
     ${SYSTEMCTL} list-units > /dev/null 2>&1 || return
     service_name="skupper-${NAMESPACE}.service"
     service_file_suffix="container"
-    [ "${SKUPPER_PLATFORM}" = "systemd" ] && service_file_suffix="systemd"
+    [ "${SKUPPER_PLATFORM}" = "linux" ] && service_file_suffix="linux"
     service_file="${SKUPPER_OUTPUT_PATH}/namespaces/${NAMESPACE}/internal/scripts/skupper.service.${service_file_suffix}"
 
     if [ ! -f "${service_file}" ]; then
@@ -172,7 +172,7 @@ remove_site() {
 
     echo "Removing Skupper site definition for ${SITE_NAME} from namespace ${NAMESPACE}"
     SKUPPER_PLATFORM=$(grep '^platform: ' "${PLATFORM_FILE}" | sed -e 's/.*: //g')
-    if [ "${SKUPPER_PLATFORM}" != "systemd" ]; then
+    if [ "${SKUPPER_PLATFORM}" != "linux" ]; then
         # removing router container
         ${SKUPPER_PLATFORM} rm -f "${NAMESPACE}-skupper-router"
     fi
@@ -199,9 +199,9 @@ set_router_access_port() {
 
 create_sed_script() {
     ssl_profile_base_path="\${SSL_PROFILE_BASE_PATH}"
-    [ "${SKUPPER_PLATFORM}" = "systemd" ] && ssl_profile_base_path="${NAMESPACES_PATH}/${NAMESPACE}"
+    [ "${SKUPPER_PLATFORM}" = "linux" ] && ssl_profile_base_path="${NAMESPACES_PATH}/${NAMESPACE}"
     container_engine="podman"
-    [ "${SKUPPER_PLATFORM}" != "systemd" ] && container_engine="${SKUPPER_PLATFORM}"
+    [ "${SKUPPER_PLATFORM}" != "linux" ] && container_engine="${SKUPPER_PLATFORM}"
     # populating sed script
     # note that this is processed a golang template prior to
     # being inserted into the bundle
@@ -237,7 +237,7 @@ show_token_info() {
 }
 
 create_containers() {
-    [ "${SKUPPER_PLATFORM}" = "systemd" ] && return
+    [ "${SKUPPER_PLATFORM}" = "linux" ] && return
     "${NAMESPACES_PATH:?}/${NAMESPACE:?}/internal/scripts/containers_create.sh"
 }
 
diff --git a/pkg/nonkube/api/site_state.go b/pkg/nonkube/api/site_state.go
index d3291c7c..9a84f960 100644
--- a/pkg/nonkube/api/site_state.go
+++ b/pkg/nonkube/api/site_state.go
@@ -273,6 +273,7 @@ func (s *SiteState) ToRouterConfig(sslProfileBasePath string, platform string) q
 		Name:      routerName,
 		Namespace: s.GetNamespace(),
 		Platform:  platform,
+		Version:   version.Version,
 	}
 
 	// override metadata
diff --git a/pkg/nonkube/bootstrap/bootstrap.go b/pkg/nonkube/bootstrap/bootstrap.go
index b6ff0f6f..18bba171 100644
--- a/pkg/nonkube/bootstrap/bootstrap.go
+++ b/pkg/nonkube/bootstrap/bootstrap.go
@@ -13,7 +13,7 @@ import (
 	"github.com/skupperproject/skupper/pkg/nonkube/bundle"
 	"github.com/skupperproject/skupper/pkg/nonkube/common"
 	"github.com/skupperproject/skupper/pkg/nonkube/compat"
-	"github.com/skupperproject/skupper/pkg/nonkube/systemd"
+	"github.com/skupperproject/skupper/pkg/nonkube/linux"
 	"github.com/skupperproject/skupper/pkg/utils"
 )
 
@@ -126,8 +126,8 @@ func Bootstrap(config *Config) (*api.SiteState, error) {
 			Strategy: internalbundle.BundleStrategy(config.BundleStrategy),
 			Platform: config.Platform,
 		}
-	} else if config.Platform == types.PlatformSystemd {
-		siteStateRenderer = &systemd.SiteStateRenderer{}
+	} else if config.Platform == types.PlatformLinux {
+		siteStateRenderer = &linux.SiteStateRenderer{}
 	} else {
 		siteStateRenderer = &compat.SiteStateRenderer{
 			Platform: config.Platform,
diff --git a/pkg/nonkube/bundle/systemd.go b/pkg/nonkube/bundle/systemd.go
index 9b2200f1..192fd8a1 100644
--- a/pkg/nonkube/bundle/systemd.go
+++ b/pkg/nonkube/bundle/systemd.go
@@ -18,7 +18,7 @@ func CreateSystemdServices(siteState *api.SiteState) error {
 	var err error
 	var logger = common.NewLogger()
 	serviceTemplates := map[string]string{
-		"systemd":   common.SystemdServiceTemplate,
+		"linux":     common.SystemdServiceTemplate,
 		"container": common.SystemdContainerServiceTemplate,
 	}
 	scriptsPath := api.GetInternalBundleOutputPath(siteState.Site.Namespace, api.ScriptsPath)
diff --git a/pkg/nonkube/common/systemd.go b/pkg/nonkube/common/systemd.go
index 18f2cddb..0cd3340b 100644
--- a/pkg/nonkube/common/systemd.go
+++ b/pkg/nonkube/common/systemd.go
@@ -90,7 +90,7 @@ func (s *systemdServiceInfo) Create() error {
 	var buf = new(bytes.Buffer)
 	var service *template.Template
 	logger.Debug("using service template for:", slog.String("platform", s.platform))
-	if s.platform == string(types.PlatformSystemd) {
+	if s.platform == string(types.PlatformLinux) {
 		service = template.Must(template.New(s.GetServiceName()).Parse(SystemdServiceTemplate))
 	} else {
 		service = template.Must(template.New(s.GetServiceName()).Parse(SystemdContainerServiceTemplate))
diff --git a/pkg/nonkube/systemd/site_state_renderer.go b/pkg/nonkube/linux/site_state_renderer.go
similarity index 96%
rename from pkg/nonkube/systemd/site_state_renderer.go
rename to pkg/nonkube/linux/site_state_renderer.go
index 6f1d42f9..e33c4ef6 100644
--- a/pkg/nonkube/systemd/site_state_renderer.go
+++ b/pkg/nonkube/linux/site_state_renderer.go
@@ -1,4 +1,4 @@
-package systemd
+package linux
 
 import (
 	"fmt"
@@ -87,7 +87,7 @@ func (s *SiteStateRenderer) Render(loadedSiteState *api.SiteState, reload bool)
 	siteHome := api.GetHostSiteHome(s.siteState.Site)
 	s.configRenderer = &common.FileSystemConfigurationRenderer{
 		SslProfileBasePath: siteHome,
-		Platform:           string(types.PlatformSystemd),
+		Platform:           string(types.PlatformLinux),
 	}
 	err = s.configRenderer.Render(s.siteState)
 	if err != nil {
@@ -119,7 +119,7 @@ func (s *SiteStateRenderer) loadExistingSiteId(siteState *api.SiteState) error {
 
 func (s *SiteStateRenderer) createSystemdService() error {
 	// Creating systemd user service
-	systemd, err := common.NewSystemdServiceInfo(s.siteState, string(types.PlatformSystemd))
+	systemd, err := common.NewSystemdServiceInfo(s.siteState, string(types.PlatformLinux))
 	if err != nil {
 		return err
 	}
@@ -140,7 +140,7 @@ func (s *SiteStateRenderer) createSystemdService() error {
 
 func (s *SiteStateRenderer) removeSystemdService() error {
 	// Removing systemd user service
-	systemd, err := common.NewSystemdServiceInfo(s.loadedSiteState, string(types.PlatformSystemd))
+	systemd, err := common.NewSystemdServiceInfo(s.loadedSiteState, string(types.PlatformLinux))
 	if err != nil {
 		return err
 	}
diff --git a/pkg/qdr/qdr.go b/pkg/qdr/qdr.go
index 1463e810..b49838f3 100644
--- a/pkg/qdr/qdr.go
+++ b/pkg/qdr/qdr.go
@@ -462,6 +462,7 @@ type SiteConfig struct {
 	Provider  string `json:"provider,omitempty"`
 	Platform  string `json:"platform,omitempty"`
 	Namespace string `json:"namespace,omitempty"`
+	Version   string `json:"version,omitempty"`
 }
 
 func convert(from interface{}, to interface{}) error {


Test patch:
diff --git a/cmd/network-observer/internal/server/address_test.go b/cmd/network-observer/internal/server/address_test.go
index 992c4ce5..22792d47 100644
--- a/cmd/network-observer/internal/server/address_test.go
+++ b/cmd/network-observer/internal/server/address_test.go
@@ -27,7 +27,7 @@ func TestAddresses(t *testing.T) {
 		ExpectOK             bool
 		ExpectCount          int
 		ExpectTimeRangeCount int
-		ExpectResults        func(t *testing.T, results []api.AddressRecord)
+		ExpectResults        func(t *testing.T, results []api.ServiceRecord)
 	}{
 		{ExpectOK: true},
 		{
@@ -44,10 +44,10 @@ func TestAddresses(t *testing.T) {
 			),
 			ExpectOK:    true,
 			ExpectCount: 3,
-			ExpectResults: func(t *testing.T, results []api.AddressRecord) {
+			ExpectResults: func(t *testing.T, results []api.ServiceRecord) {
 				for _, result := range results {
 					if result.Identity == "addr-1" {
-						assert.DeepEqual(t, result, api.AddressRecord{
+						assert.DeepEqual(t, result, api.ServiceRecord{
 							Identity:                     "addr-1",
 							Name:                         "pizza",
 							ConnectorCount:               2,
@@ -77,7 +77,7 @@ func TestAddresses(t *testing.T) {
 			},
 			ExpectOK:    true,
 			ExpectCount: 3,
-			ExpectResults: func(t *testing.T, results []api.AddressRecord) {
+			ExpectResults: func(t *testing.T, results []api.ServiceRecord) {
 				assert.Equal(t, results[2].Identity, "addr-1", "Expected addr-1 to be last with sortBy isBound.asc")
 				assert.Equal(t, results[2].IsBound, true, "Expected addr-1 to be bound")
 			},
@@ -95,7 +95,7 @@ func TestAddresses(t *testing.T) {
 			},
 			ExpectOK:    true,
 			ExpectCount: 3,
-			ExpectResults: func(t *testing.T, results []api.AddressRecord) {
+			ExpectResults: func(t *testing.T, results []api.ServiceRecord) {
 				assert.Equal(t, results[0].Identity, "addr-1", "Expected addr-1 to be first with sortBy isBound.asc")
 				assert.Equal(t, results[0].IsBound, true, "Expected addr-1 to be bound")
 			},
@@ -109,7 +109,7 @@ func TestAddresses(t *testing.T) {
 			for _, r := range tc.Records {
 				graph.(reset).Reindex(r.Record)
 			}
-			resp, err := c.AddressesWithResponse(context.TODO(), withParameters(tc.Parameters))
+			resp, err := c.ServicesWithResponse(context.TODO(), withParameters(tc.Parameters))
 			assert.Check(t, err)
 			if tc.ExpectOK {
 				assert.Equal(t, resp.StatusCode(), 200)
diff --git a/cmd/network-observer/internal/server/connector_test.go b/cmd/network-observer/internal/server/connector_test.go
index 5ab96f72..f89e147a 100644
--- a/cmd/network-observer/internal/server/connector_test.go
+++ b/cmd/network-observer/internal/server/connector_test.go
@@ -34,7 +34,7 @@ func TestConnectors(t *testing.T) {
 				assert.DeepEqual(t, result, api.ConnectorRecord{
 					Identity: "c1", Name: "unknown", Parent: "unknown",
 					SiteName: "unknown", SiteId: "unknown",
-					Protocol: "unknown", Address: "unknown", DestHost: "unknown",
+					Protocol: "unknown", RoutingKey: "unknown", DestHost: "unknown",
 					ProcessId: "", DestPort: "unknown",
 				})
 			},
@@ -65,7 +65,7 @@ func TestConnectors(t *testing.T) {
 				assert.DeepEqual(t, result, api.ConnectorRecord{
 					Identity: "c1", Name: "conn-one", Parent: "r1",
 					SiteName: "one", SiteId: "s1",
-					Protocol: "tp", Address: "trombone", DestHost: "10.0.four.two",
+					Protocol: "tp", RoutingKey: "trombone", DestHost: "10.0.four.two",
 					ProcessId: "p1", Target: ptrTo("proc-one"), DestPort: "amqp",
 				})
 			},
diff --git a/cmd/network-observer/internal/server/process_test.go b/cmd/network-observer/internal/server/process_test.go
index e20c3d4e..57028b3e 100644
--- a/cmd/network-observer/internal/server/process_test.go
+++ b/cmd/network-observer/internal/server/process_test.go
@@ -71,7 +71,7 @@ func TestProcesses(t *testing.T) {
 					Identity:       "1",
 					Parent:         "site-1",
 					ParentName:     "site one",
-					Addresses:      ptrTo([]api.AtmarkDelimitedString{api.AtmarkDelimitedString("icecream@icecream-addr-id@tcp"), api.AtmarkDelimitedString("pizza@pizza-addr-id@tcp")}),
+					Services:       ptrTo([]api.AtmarkDelimitedString{api.AtmarkDelimitedString("icecream@icecream-addr-id@tcp"), api.AtmarkDelimitedString("pizza@pizza-addr-id@tcp")}),
 					GroupName:      "group-one",
 					GroupIdentity:  "group-1-id",
 					ProcessBinding: api.Unbound,
@@ -96,7 +96,7 @@ func TestProcesses(t *testing.T) {
 					Identity:       "1",
 					Parent:         "site-1",
 					ParentName:     "site one",
-					Addresses:      ptrTo([]api.AtmarkDelimitedString{api.AtmarkDelimitedString("icecream@icecream-addr-id@tcp"), api.AtmarkDelimitedString("pizza@pizza-addr-id@tcp")}),
+					Services:       ptrTo([]api.AtmarkDelimitedString{api.AtmarkDelimitedString("icecream@icecream-addr-id@tcp"), api.AtmarkDelimitedString("pizza@pizza-addr-id@tcp")}),
 					GroupName:      "group-one",
 					GroupIdentity:  "group-1-id",
 					ProcessBinding: api.Bound,
@@ -126,14 +126,14 @@ func TestProcesses(t *testing.T) {
 		}, {
 			Records: exProcessWithAddresses(),
 			Parameters: map[string][]string{
-				"addresses": {"pizza-addr-id"},
+				"services": {"pizza-addr-id"},
 			},
 			ExpectOK:    true,
 			ExpectCount: 1,
 		}, {
 			Records: exProcessWithAddresses(),
 			Parameters: map[string][]string{
-				"addresses": {"gumbo"},
+				"services": {"gumbo"},
 			},
 			ExpectOK:    true,
 			ExpectCount: 0,
diff --git a/cmd/network-observer/internal/server/processpairs_test.go b/cmd/network-observer/internal/server/processpairs_test.go
index cbd1d3a6..152023ae 100644
--- a/cmd/network-observer/internal/server/processpairs_test.go
+++ b/cmd/network-observer/internal/server/processpairs_test.go
@@ -42,7 +42,7 @@ func TestProcessPairsByAddress(t *testing.T) {
 			for _, r := range tc.Records {
 				graph.(reset).Reindex(r.Record)
 			}
-			resp, err := c.ProcessPairsByAddressWithResponse(context.TODO(), tc.ID, withParameters(tc.Parameters))
+			resp, err := c.ProcessPairsByServiceWithResponse(context.TODO(), tc.ID, withParameters(tc.Parameters))
 			assert.Check(t, err)
 			if tc.ExpectOK {
 				assert.Equal(t, resp.StatusCode(), 200)
diff --git a/internal/cmd/skupper/system/nonkube/system_reload_test.go b/internal/cmd/skupper/system/nonkube/system_reload_test.go
index f4f6ea51..4fac99b3 100644
--- a/internal/cmd/skupper/system/nonkube/system_reload_test.go
+++ b/internal/cmd/skupper/system/nonkube/system_reload_test.go
@@ -31,7 +31,7 @@ func TestCmdSystemReload_ValidateInput(t *testing.T) {
 		t.Run(test.name, func(t *testing.T) {
 
 			command := &CmdSystemReload{}
-			command.CobraCmd = common.ConfigureCobraCommand(types.PlatformSystemd, common.SkupperCmdDescription{}, command, nil)
+			command.CobraCmd = common.ConfigureCobraCommand(types.PlatformLinux, common.SkupperCmdDescription{}, command, nil)
 
 			actualErrors := command.ValidateInput(test.args)
 			actualErrorsMessages := utils.ErrorsToMessages(actualErrors)
@@ -59,9 +59,9 @@ func TestCmdSystemReload_InputToOptions(t *testing.T) {
 			expectedNamespace: "default",
 		},
 		{
-			name:              "systemd",
+			name:              "linux",
 			namespace:         "east",
-			platform:          "systemd",
+			platform:          "linux",
 			expectedBinary:    "skrouterd",
 			expectedNamespace: "east",
 		},
diff --git a/internal/cmd/skupper/system/nonkube/system_setup_test.go b/internal/cmd/skupper/system/nonkube/system_setup_test.go
index c911a0a7..4c93554a 100644
--- a/internal/cmd/skupper/system/nonkube/system_setup_test.go
+++ b/internal/cmd/skupper/system/nonkube/system_setup_test.go
@@ -40,7 +40,7 @@ func TestCmdSystemSetup_ValidateInput(t *testing.T) {
 		t.Run(test.name, func(t *testing.T) {
 
 			command := &CmdSystemSetup{}
-			command.CobraCmd = common.ConfigureCobraCommand(types.PlatformSystemd, common.SkupperCmdDescription{}, command, nil)
+			command.CobraCmd = common.ConfigureCobraCommand(types.PlatformLinux, common.SkupperCmdDescription{}, command, nil)
 
 			if test.flags != nil {
 				command.Flags = test.flags
@@ -76,12 +76,12 @@ func TestCmdSystemSetup_InputToOptions(t *testing.T) {
 			expectedNamespace: "default",
 		},
 		{
-			name: "systemd",
+			name: "linux",
 			flags: common.CommandSystemSetupFlags{
 				Path: "input-path",
 			},
 			namespace:         "east",
-			platform:          "systemd",
+			platform:          "linux",
 			expectedBinary:    "skrouterd",
 			expectedNamespace: "east",
 		},
diff --git a/internal/cmd/skupper/system/nonkube/system_start_test.go b/internal/cmd/skupper/system/nonkube/system_start_test.go
index 9f8db1c4..e6852ca6 100644
--- a/internal/cmd/skupper/system/nonkube/system_start_test.go
+++ b/internal/cmd/skupper/system/nonkube/system_start_test.go
@@ -28,7 +28,7 @@ func TestCmdSystemStart_ValidateInput(t *testing.T) {
 		t.Run(test.name, func(t *testing.T) {
 
 			command := &CmdSystemStart{}
-			command.CobraCmd = common.ConfigureCobraCommand(types.PlatformSystemd, common.SkupperCmdDescription{}, command, nil)
+			command.CobraCmd = common.ConfigureCobraCommand(types.PlatformLinux, common.SkupperCmdDescription{}, command, nil)
 
 			actualErrors := command.ValidateInput(test.args)
 			actualErrorsMessages := utils.ErrorsToMessages(actualErrors)
diff --git a/internal/cmd/skupper/system/nonkube/system_stop_test.go b/internal/cmd/skupper/system/nonkube/system_stop_test.go
index 6a133b03..82436df3 100644
--- a/internal/cmd/skupper/system/nonkube/system_stop_test.go
+++ b/internal/cmd/skupper/system/nonkube/system_stop_test.go
@@ -29,7 +29,7 @@ func TestCmdSystemStop_ValidateInput(t *testing.T) {
 		t.Run(test.name, func(t *testing.T) {
 
 			command := &CmdSystemStop{}
-			command.CobraCmd = common.ConfigureCobraCommand(types.PlatformSystemd, common.SkupperCmdDescription{}, command, nil)
+			command.CobraCmd = common.ConfigureCobraCommand(types.PlatformLinux, common.SkupperCmdDescription{}, command, nil)
 
 			actualErrors := command.ValidateInput(test.args)
 			actualErrorsMessages := utils.ErrorsToMessages(actualErrors)
diff --git a/internal/cmd/skupper/system/nonkube/system_teardown_test.go b/internal/cmd/skupper/system/nonkube/system_teardown_test.go
index 0bcd81ec..df977689 100644
--- a/internal/cmd/skupper/system/nonkube/system_teardown_test.go
+++ b/internal/cmd/skupper/system/nonkube/system_teardown_test.go
@@ -29,7 +29,7 @@ func TestCmdSystemTearDown_ValidateInput(t *testing.T) {
 		t.Run(test.name, func(t *testing.T) {
 
 			command := &CmdSystemTeardown{}
-			command.CobraCmd = common.ConfigureCobraCommand(types.PlatformSystemd, common.SkupperCmdDescription{}, command, nil)
+			command.CobraCmd = common.ConfigureCobraCommand(types.PlatformLinux, common.SkupperCmdDescription{}, command, nil)
 
 			actualErrors := command.ValidateInput(test.args)
 			actualErrorsMessages := utils.ErrorsToMessages(actualErrors)
diff --git a/internal/kube/controller/controller_test.go b/internal/kube/controller/controller_test.go
new file mode 100644
index 00000000..d2c3b1ac
--- /dev/null
+++ b/internal/kube/controller/controller_test.go
@@ -0,0 +1,1240 @@
+package controller
+
+import (
+	"context"
+	"encoding/json"
+	"flag"
+	"fmt"
+	"reflect"
+	"testing"
+
+	"github.com/google/uuid"
+	"gotest.tools/v3/assert"
+
+	appsv1 "k8s.io/api/apps/v1"
+	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/api/meta"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/apimachinery/pkg/util/intstr"
+	"k8s.io/apimachinery/pkg/util/rand"
+	"k8s.io/client-go/dynamic"
+	fakedynamic "k8s.io/client-go/dynamic/fake"
+	k8stesting "k8s.io/client-go/testing"
+
+	internalclient "github.com/skupperproject/skupper/internal/kube/client"
+	fakeclient "github.com/skupperproject/skupper/internal/kube/client/fake"
+	"github.com/skupperproject/skupper/internal/kube/resource"
+	skupperv2alpha1 "github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
+	"github.com/skupperproject/skupper/pkg/network"
+	"github.com/skupperproject/skupper/pkg/qdr"
+)
+
+type WaitFunction func(t *testing.T, clients internalclient.Clients) bool
+
+func TestGeneral(t *testing.T) {
+	fakeNetworkStatus := f.fakeNetworkStatusInfo("test")
+	testTable := []struct {
+		name                                     string
+		args                                     []string
+		env                                      map[string]string
+		k8sObjects                               []runtime.Object
+		skupperObjects                           []runtime.Object
+		waitFunctions                            []WaitFunction
+		expectedDynamicResources                 map[schema.GroupVersionResource]*unstructured.Unstructured
+		expectedRouterConfig                     []*RouterConfig
+		expectedServices                         []*corev1.Service
+		expectedRouterAccesses                   []*skupperv2alpha1.RouterAccess
+		expectedSecuredAccesses                  []*skupperv2alpha1.SecuredAccess
+		expectedSiteStatuses                     []*skupperv2alpha1.Site
+		expectedListenerStatuses                 []*skupperv2alpha1.Listener
+		expectedConnectorStatuses                []*skupperv2alpha1.Connector
+		expectedAttachedConnectorStatuses        []*skupperv2alpha1.AttachedConnector
+		expectedAttachedConnectorBindingStatuses []*skupperv2alpha1.AttachedConnectorBinding
+	}{
+		{
+			name: "simple site",
+			skupperObjects: []runtime.Object{
+				f.site("mysite", "test", "", false, false),
+			},
+			expectedSiteStatuses: []*skupperv2alpha1.Site{
+				f.siteStatus("mysite", "test", skupperv2alpha1.StatusPending, "Not Running", f.condition(skupperv2alpha1.CONDITION_TYPE_CONFIGURED, metav1.ConditionTrue, "Ready", "OK")),
+			},
+			expectedDynamicResources: map[schema.GroupVersionResource]*unstructured.Unstructured{
+				resource.DeploymentResource(): f.routerDeployment("skupper-router", "test"),
+			},
+		},
+		{
+			name: "site with running router pods",
+			k8sObjects: []runtime.Object{
+				f.pod("skupper-router-1", "test", map[string]string{"skupper.io/component": "router", "skupper.io/type": "site"}, nil,
+					f.podStatus("10.1.1.10", corev1.PodRunning, f.podCondition(corev1.PodReady, corev1.ConditionTrue))),
+			},
+			skupperObjects: []runtime.Object{
+				f.site("mysite", "test", "", false, false),
+			},
+			expectedSiteStatuses: []*skupperv2alpha1.Site{
+				f.siteStatus("mysite", "test", skupperv2alpha1.StatusReady, "OK", f.condition(skupperv2alpha1.CONDITION_TYPE_CONFIGURED, metav1.ConditionTrue, "Ready", "OK"),
+					f.condition(skupperv2alpha1.CONDITION_TYPE_RUNNING, metav1.ConditionTrue, "Ready", "OK")),
+			},
+			expectedDynamicResources: map[schema.GroupVersionResource]*unstructured.Unstructured{
+				resource.DeploymentResource(): f.routerDeployment("skupper-router", "test"),
+			},
+		},
+		{
+			name: "site with network status",
+			k8sObjects: []runtime.Object{
+				f.skupperNetworkStatus("test", fakeNetworkStatus.info()),
+				f.pod("skupper-router-1", "test", map[string]string{"skupper.io/component": "router", "skupper.io/type": "site"}, nil,
+					f.podStatus("10.1.1.10", corev1.PodRunning, f.podCondition(corev1.PodReady, corev1.ConditionTrue))),
+			},
+			skupperObjects: []runtime.Object{
+				f.site("mysite", "test", "", false, false),
+			},
+			waitFunctions: []WaitFunction{
+				isSiteStatusConditionTrue("mysite", "test", skupperv2alpha1.CONDITION_TYPE_RUNNING),
+				isSiteNetworkStatusSet("mysite", "test"),
+			},
+			expectedSiteStatuses: []*skupperv2alpha1.Site{
+				f.addNetworkStatus(f.siteStatus("mysite", "test", skupperv2alpha1.StatusReady, "OK"), fakeNetworkStatus),
+			},
+			expectedDynamicResources: map[schema.GroupVersionResource]*unstructured.Unstructured{
+				resource.DeploymentResource(): f.routerDeployment("skupper-router", "test"),
+			},
+		},
+		{
+			name: "site with default link access",
+			k8sObjects: []runtime.Object{
+				f.pod("skupper-router-1", "test", map[string]string{"skupper.io/component": "router", "skupper.io/type": "site"}, nil,
+					f.podStatus("10.1.1.10", corev1.PodRunning, f.podCondition(corev1.PodReady, corev1.ConditionTrue))),
+			},
+			skupperObjects: []runtime.Object{
+				f.site("mysvc", "test", "default", false, false),
+			},
+			expectedSiteStatuses: []*skupperv2alpha1.Site{
+				f.siteStatus("mysvc", "test", skupperv2alpha1.StatusPending, "Pending", f.condition(skupperv2alpha1.CONDITION_TYPE_CONFIGURED, metav1.ConditionTrue, "Ready", "OK"),
+					f.condition(skupperv2alpha1.CONDITION_TYPE_RUNNING, metav1.ConditionTrue, "Ready", "OK")),
+			},
+			expectedDynamicResources: map[schema.GroupVersionResource]*unstructured.Unstructured{
+				resource.DeploymentResource(): f.routerDeployment("skupper-router", "test"),
+			},
+			expectedRouterAccesses: []*skupperv2alpha1.RouterAccess{
+				f.routerAccess("skupper-router", "test", "", "skupper-site-server", true, "skupper-site-ca", f.role("inter-router", 55671), f.role("edge", 45671)),
+			},
+		},
+		{
+			name: "site with specific link access",
+			env: map[string]string{
+				"SKUPPER_ENABLED_ACCESS_TYPES": "route,loadbalancer,ingress-nginx",
+			},
+			k8sObjects: []runtime.Object{
+				f.pod("skupper-router-1", "test", map[string]string{"skupper.io/component": "router", "skupper.io/type": "site"}, nil,
+					f.podStatus("10.1.1.10", corev1.PodRunning, f.podCondition(corev1.PodReady, corev1.ConditionTrue))),
+			},
+			skupperObjects: []runtime.Object{
+				f.site("mysvc", "test", "ingress-nginx", false, false),
+			},
+			expectedSiteStatuses: []*skupperv2alpha1.Site{
+				f.siteStatus("mysvc", "test", skupperv2alpha1.StatusPending, "Pending", f.condition(skupperv2alpha1.CONDITION_TYPE_CONFIGURED, metav1.ConditionTrue, "Ready", "OK"),
+					f.condition(skupperv2alpha1.CONDITION_TYPE_RUNNING, metav1.ConditionTrue, "Ready", "OK")),
+			},
+			expectedDynamicResources: map[schema.GroupVersionResource]*unstructured.Unstructured{
+				resource.DeploymentResource(): f.routerDeployment("skupper-router", "test"),
+			},
+			expectedRouterAccesses: []*skupperv2alpha1.RouterAccess{
+				f.routerAccess("skupper-router", "test", "ingress-nginx", "skupper-site-server", true, "skupper-site-ca", f.role("inter-router", 55671), f.role("edge", 45671)),
+			},
+			expectedSecuredAccesses: []*skupperv2alpha1.SecuredAccess{
+				f.securedAccess("skupper-router", "test", "ingress-nginx", f.routerSelectorWithGroup("skupper-router"), "skupper-site-server", "skupper-site-ca",
+					f.securedAccessPort("inter-router", 55671), f.securedAccessPort("edge", 45671)),
+			},
+		},
+		{
+			name: "ha site with default link access",
+			k8sObjects: []runtime.Object{
+				f.pod("skupper-router-1", "test", map[string]string{"skupper.io/component": "router", "skupper.io/type": "site"}, nil,
+					f.podStatus("10.1.1.10", corev1.PodRunning, f.podCondition(corev1.PodReady, corev1.ConditionTrue))),
+			},
+			skupperObjects: []runtime.Object{
+				f.site("mysvc", "test", "default", true, false),
+			},
+			expectedSiteStatuses: []*skupperv2alpha1.Site{
+				f.siteStatus("mysvc", "test", skupperv2alpha1.StatusPending, "Pending", f.condition(skupperv2alpha1.CONDITION_TYPE_CONFIGURED, metav1.ConditionTrue, "Ready", "OK"),
+					f.condition(skupperv2alpha1.CONDITION_TYPE_RUNNING, metav1.ConditionTrue, "Ready", "OK")),
+			},
+			expectedDynamicResources: map[schema.GroupVersionResource]*unstructured.Unstructured{
+				resource.DeploymentResource(): f.routerDeployment("skupper-router", "test"),
+				resource.DeploymentResource(): f.routerDeployment("skupper-router-2", "test"),
+			},
+			expectedRouterAccesses: []*skupperv2alpha1.RouterAccess{
+				f.routerAccess("skupper-router", "test", "", "skupper-site-server", true, "skupper-site-ca", f.role("inter-router", 55671), f.role("edge", 45671)),
+			},
+			expectedSecuredAccesses: []*skupperv2alpha1.SecuredAccess{
+				f.securedAccess("skupper-router", "test", "", f.routerSelectorWithGroup("skupper-router"), "skupper-site-server", "skupper-site-ca",
+					f.securedAccessPort("inter-router", 55671), f.securedAccessPort("edge", 45671)),
+				f.securedAccess("skupper-router-2", "test", "", f.routerSelectorWithGroup("skupper-router-2"), "skupper-site-server", "skupper-site-ca",
+					f.securedAccessPort("inter-router", 55671), f.securedAccessPort("edge", 45671)),
+			},
+		},
+		{
+			name: "site with router access",
+			env: map[string]string{
+				"SKUPPER_ENABLED_ACCESS_TYPES": "route,loadbalancer,ingress-nginx",
+			},
+			k8sObjects: []runtime.Object{
+				f.pod("skupper-router-1", "test", map[string]string{"skupper.io/component": "router", "skupper.io/type": "site"}, nil,
+					f.podStatus("10.1.1.10", corev1.PodRunning, f.podCondition(corev1.PodReady, corev1.ConditionTrue))),
+			},
+			skupperObjects: []runtime.Object{
+				f.site("mysvc", "test", "ingress-nginx", false, false),
+				f.routerAccess("foo", "test", "ingress-nginx", "foo-server", true, "foo-ca", f.role("inter-router", 55555)),
+			},
+			expectedSiteStatuses: []*skupperv2alpha1.Site{
+				f.siteStatus("mysvc", "test", skupperv2alpha1.StatusPending, "Pending", f.condition(skupperv2alpha1.CONDITION_TYPE_CONFIGURED, metav1.ConditionTrue, "Ready", "OK"),
+					f.condition(skupperv2alpha1.CONDITION_TYPE_RUNNING, metav1.ConditionTrue, "Ready", "OK")),
+			},
+			expectedDynamicResources: map[schema.GroupVersionResource]*unstructured.Unstructured{
+				resource.DeploymentResource(): f.routerDeployment("skupper-router", "test"),
+			},
+			expectedSecuredAccesses: []*skupperv2alpha1.SecuredAccess{
+				f.securedAccess("foo", "test", "ingress-nginx", f.routerSelectorWithGroup("skupper-router"), "foo-server", "foo-ca",
+					f.securedAccessPort("inter-router", 55555)),
+			},
+		},
+		{
+			name: "site with listener",
+			skupperObjects: []runtime.Object{
+				f.site("mysvc", "test", "", false, false),
+				f.listener("mylistener", "test", "mysvc", 8080),
+			},
+			expectedSiteStatuses: []*skupperv2alpha1.Site{
+				f.siteStatus("mysvc", "test", skupperv2alpha1.StatusPending, "Not Running", f.condition(skupperv2alpha1.CONDITION_TYPE_CONFIGURED, metav1.ConditionTrue, "Ready", "OK")),
+			},
+			expectedDynamicResources: map[schema.GroupVersionResource]*unstructured.Unstructured{
+				resource.DeploymentResource(): f.routerDeployment("skupper-router", "test"),
+			},
+			expectedRouterConfig: []*RouterConfig{
+				f.routerConfig("skupper-router", "test").tcpListener("mylistener", "1024", "", ""),
+			},
+			expectedServices: []*corev1.Service{
+				f.service("mysvc", "test", f.routerSelector(true), f.servicePort("mylistener", 8080, 1024)),
+			},
+		},
+		{
+			name: "expose pods by name",
+			k8sObjects: []runtime.Object{
+				f.skupperNetworkStatus("test", f.networkStatusInfo("mysite", "test", nil, map[string]string{"mysvc": "mysvc", "mysvc.mypod-1": "10.1.1.10", "mysvc.mypod-2": "10.1.1.11"}).info()),
+				f.pod("mypod-1", "test", map[string]string{"app": "foo"}, nil, f.podStatus("10.1.1.10", corev1.PodRunning, f.podCondition(corev1.PodReady, corev1.ConditionTrue))),
+				f.pod("mypod-2", "test", map[string]string{"app": "foo"}, nil, f.podStatus("10.1.1.11", corev1.PodRunning, f.podCondition(corev1.PodReady, corev1.ConditionTrue))),
+			},
+			skupperObjects: []runtime.Object{
+				f.site("mysite", "test", "", false, false),
+				f.connectorWithExposePodsByName("myconnector", "test", "mysvc", "app=foo", 8080),
+				f.listenerWithExposePodsByName("mylistener", "test", "mysvc", "mysvc", 8080),
+			},
+			waitFunctions: []WaitFunction{
+				isConnectorStatusConditionTrue("myconnector", "test", skupperv2alpha1.CONDITION_TYPE_CONFIGURED),
+				isListenerStatusConditionTrue("mylistener", "test", skupperv2alpha1.CONDITION_TYPE_CONFIGURED),
+			},
+			expectedSiteStatuses: []*skupperv2alpha1.Site{
+				f.siteStatus("mysite", "test", skupperv2alpha1.StatusPending, "Not Running", f.condition(skupperv2alpha1.CONDITION_TYPE_CONFIGURED, metav1.ConditionTrue, "Ready", "OK")),
+			},
+			expectedDynamicResources: map[schema.GroupVersionResource]*unstructured.Unstructured{
+				resource.DeploymentResource(): f.routerDeployment("skupper-router", "test"),
+			},
+			expectedRouterConfig: []*RouterConfig{
+				f.routerConfig(
+					"skupper-router",
+					"test",
+				).tcpListener(
+					"mylistener", "1024", "mysvc", "",
+				).tcpListener(
+					"mylistener@mypod-1", "*", "mysvc.mypod-1", "",
+				).tcpListener(
+					"mylistener@mypod-2", "*", "mysvc.mypod-2", "",
+				).tcpConnector(
+					"myconnector@10.1.1.10", "10.1.1.10", "8080", "mysvc", "",
+				).tcpConnector(
+					"myconnector@10.1.1.11", "10.1.1.11", "8080", "mysvc", "",
+				).tcpConnector(
+					"myconnector@mypod-1", "10.1.1.10", "8080", "mysvc.mypod-1", "",
+				).tcpConnector(
+					"myconnector@mypod-2", "10.1.1.11", "8080", "mysvc.mypod-2", "",
+				),
+			},
+			expectedServices: []*corev1.Service{
+				f.service("mysvc", "test", f.routerSelector(true), f.servicePort("mylistener", 8080, 1024)),
+				f.service("mypod-1", "test", f.routerSelector(true), f.servicePortU("mylistener", 8080)),
+				f.service("mypod-2", "test", f.routerSelector(true), f.servicePortU("mylistener", 8080)),
+			},
+		},
+		{
+			name: "listener with no site",
+			skupperObjects: []runtime.Object{
+				f.listener("mylistener", "test", "mysvc", 8080),
+			},
+			expectedListenerStatuses: []*skupperv2alpha1.Listener{
+				f.listenerStatus("mylistener", "test", skupperv2alpha1.StatusError, "No active site in namespace",
+					f.condition(skupperv2alpha1.CONDITION_TYPE_CONFIGURED, metav1.ConditionFalse, "Error", "No active site in namespace")),
+			},
+		},
+		{
+			name: "site and connector with host field",
+			skupperObjects: []runtime.Object{
+				f.site("mysvc", "test", "", false, false),
+				f.connector("myconnector", "test", "mysvc", 8080),
+			},
+			expectedSiteStatuses: []*skupperv2alpha1.Site{
+				f.siteStatus("mysvc", "test", skupperv2alpha1.StatusPending, "Not Running", f.condition(skupperv2alpha1.CONDITION_TYPE_CONFIGURED, metav1.ConditionTrue, "Ready", "OK")),
+			},
+			expectedDynamicResources: map[schema.GroupVersionResource]*unstructured.Unstructured{
+				resource.DeploymentResource(): f.routerDeployment("skupper-router", "test"),
+			},
+			expectedRouterConfig: []*RouterConfig{
+				f.routerConfig("skupper-router", "test").tcpConnector("myconnector@mysvc", "mysvc", "8080", "", ""),
+			},
+		},
+		{
+			name: "site and connector with selector field",
+			k8sObjects: []runtime.Object{
+				f.pod("mypod-1", "test", map[string]string{"app": "foo"}, nil, f.podStatus("10.1.1.10", corev1.PodRunning, f.podCondition(corev1.PodReady, corev1.ConditionTrue))),
+				f.pod("mypod-2", "test", map[string]string{"app": "foo"}, nil, f.podStatus("10.1.1.11", corev1.PodPending)), //not running
+				f.pod("mypod-3", "test", map[string]string{"app": "foo"}, nil, f.podStatus("10.1.1.12", corev1.PodRunning)), //not ready
+			},
+			skupperObjects: []runtime.Object{
+				f.site("mysvc", "test", "", false, false),
+				f.connectorWithSelector("myconnector", "test", "app=foo", 8080),
+			},
+			waitFunctions: []WaitFunction{
+				isConnectorStatusConditionTrue("myconnector", "test", skupperv2alpha1.CONDITION_TYPE_CONFIGURED),
+			},
+			expectedSiteStatuses: []*skupperv2alpha1.Site{
+				f.siteStatus("mysvc", "test", skupperv2alpha1.StatusPending, "Not Running", f.condition(skupperv2alpha1.CONDITION_TYPE_CONFIGURED, metav1.ConditionTrue, "Ready", "OK")),
+			},
+			expectedDynamicResources: map[schema.GroupVersionResource]*unstructured.Unstructured{
+				resource.DeploymentResource(): f.routerDeployment("skupper-router", "test"),
+			},
+			expectedRouterConfig: []*RouterConfig{
+				f.routerConfig("skupper-router", "test").tcpConnector("myconnector@10.1.1.10", "10.1.1.10", "8080", "", ""),
+			},
+		},
+		{
+			name: "connector with no site",
+			skupperObjects: []runtime.Object{
+				f.connector("myconnector", "test", "mysvc", 8080),
+			},
+			expectedConnectorStatuses: []*skupperv2alpha1.Connector{
+				f.connectorStatus("myconnector", "test", skupperv2alpha1.StatusError, "No active site in namespace",
+					f.condition(skupperv2alpha1.CONDITION_TYPE_CONFIGURED, metav1.ConditionFalse, "Error", "No active site in namespace")),
+			},
+		},
+		{
+			name: "site with attached connector",
+			k8sObjects: []runtime.Object{
+				f.pod("mypod-1", "other", map[string]string{"app": "foo"}, nil, f.podStatus("10.1.1.10", corev1.PodRunning, f.podCondition(corev1.PodReady, corev1.ConditionTrue))),
+				f.pod("mypod-2", "other", map[string]string{"app": "foo"}, nil, f.podStatus("10.1.1.11", corev1.PodPending)), //not running
+				f.pod("mypod-3", "other", map[string]string{"app": "foo"}, nil, f.podStatus("10.1.1.12", corev1.PodRunning)), //not ready
+			},
+			skupperObjects: []runtime.Object{
+				f.site("mysvc", "test", "", false, false),
+				f.attachedConnector("myconnector", "other", "test", "app=foo", 8080),
+				f.attachedConnectorBinding("myconnector", "test", "other"),
+			},
+			waitFunctions: []WaitFunction{
+				isAttachedConnectorStatusConditionTrue("myconnector", "other", skupperv2alpha1.CONDITION_TYPE_CONFIGURED),
+			},
+			expectedSiteStatuses: []*skupperv2alpha1.Site{
+				f.siteStatus("mysvc", "test", skupperv2alpha1.StatusPending, "Not Running", f.condition(skupperv2alpha1.CONDITION_TYPE_CONFIGURED, metav1.ConditionTrue, "Ready", "OK")),
+			},
+			expectedDynamicResources: map[schema.GroupVersionResource]*unstructured.Unstructured{
+				resource.DeploymentResource(): f.routerDeployment("skupper-router", "test"),
+			},
+			expectedRouterConfig: []*RouterConfig{
+				f.routerConfig("skupper-router", "test").tcpConnector("myconnector@10.1.1.10", "10.1.1.10", "8080", "", ""),
+			},
+		},
+		/* TODO: Fix
+		{
+			name: "attached connector with no site",
+			k8sObjects: []runtime.Object{
+				f.pod("mypod-1", "other", map[string]string{"app":"foo"}, nil, f.podStatus("10.1.1.10", corev1.PodRunning, f.podCondition(corev1.PodReady, corev1.ConditionTrue))),
+				f.pod("mypod-2", "other", map[string]string{"app":"foo"}, nil, f.podStatus("10.1.1.11", corev1.PodPending)),//not running
+				f.pod("mypod-3", "other", map[string]string{"app":"foo"}, nil, f.podStatus("10.1.1.12", corev1.PodRunning)),//not ready
+			},
+			skupperObjects: []runtime.Object{
+				f.attachedConnector("myconnector", "other", "test", "app=foo", 8080),
+				f.attachedConnectorBinding("myconnector", "test", "other"),
+			},
+			expectedAttachedConnectorBindingStatuses: []*skupperv2alpha1.AttachedConnectorBinding{
+				f.attachedConnectorBindingStatus("myconnector", "test", skupperv2alpha1.StatusError, "No active site in namespace",
+					f.condition(skupperv2alpha1.CONDITION_TYPE_CONFIGURED, metav1.ConditionFalse, "Error", "No active site in namespace")),
+			},
+		},
+		*/
+		{
+			name: "attached connector mismatch",
+			k8sObjects: []runtime.Object{
+				f.pod("mypod-1", "other", map[string]string{"app": "foo"}, nil, f.podStatus("10.1.1.10", corev1.PodRunning, f.podCondition(corev1.PodReady, corev1.ConditionTrue))),
+			},
+			skupperObjects: []runtime.Object{
+				f.site("mysvc", "test", "", false, false),
+				f.attachedConnector("foo", "other", "test", "app=foo", 8080),
+				f.attachedConnectorBinding("bar", "test", "other"),
+			},
+			expectedAttachedConnectorStatuses: []*skupperv2alpha1.AttachedConnector{
+				f.attachedConnectorStatus("foo", "other", skupperv2alpha1.StatusError, "No matching AttachedConnectorBinding in site namespace",
+					f.condition(skupperv2alpha1.CONDITION_TYPE_CONFIGURED, metav1.ConditionFalse, "Error", "No matching AttachedConnectorBinding in site namespace")),
+			},
+			expectedAttachedConnectorBindingStatuses: []*skupperv2alpha1.AttachedConnectorBinding{
+				f.attachedConnectorBindingStatus("bar", "test", skupperv2alpha1.StatusError, "No matching AttachedConnector",
+					f.condition(skupperv2alpha1.CONDITION_TYPE_CONFIGURED, metav1.ConditionFalse, "Error", "No matching AttachedConnector")),
+			},
+		},
+	}
+	for _, tt := range testTable {
+		t.Run(tt.name, func(t *testing.T) {
+			flags := &flag.FlagSet{}
+			for k, v := range tt.env {
+				t.Setenv(k, v)
+			}
+			config, err := BoundConfig(flags)
+			assert.Assert(t, err)
+			flags.Parse(tt.args)
+			clients, err := fakeclient.NewFakeClient(config.Namespace, tt.k8sObjects, tt.skupperObjects, "")
+			assert.Assert(t, err)
+			enableSSA(clients.GetDynamicClient())
+			controller, err := NewController(clients, config)
+			assert.Assert(t, err)
+			stopCh := make(chan struct{})
+			err = controller.init(stopCh)
+			assert.Assert(t, err)
+			for i := 0; i < len(tt.k8sObjects)+len(tt.skupperObjects); i++ {
+				controller.controller.TestProcess()
+			}
+			for _, wf := range tt.waitFunctions {
+				for !wf(t, clients) {
+					controller.controller.TestProcess()
+				}
+			}
+			for _, expected := range tt.expectedSiteStatuses {
+				actual, err := clients.GetSkupperClient().SkupperV2alpha1().Sites(expected.Namespace).Get(context.Background(), expected.Name, metav1.GetOptions{})
+				assert.Assert(t, err)
+				verifyStatus(t, expected.Status.Status, actual.Status.Status)
+				if expected.Status.Network != nil {
+					assert.DeepEqual(t, expected.Status.Network, actual.Status.Network)
+				}
+			}
+			for _, expected := range tt.expectedListenerStatuses {
+				actual, err := clients.GetSkupperClient().SkupperV2alpha1().Listeners(expected.Namespace).Get(context.Background(), expected.Name, metav1.GetOptions{})
+				assert.Assert(t, err)
+				verifyStatus(t, expected.Status.Status, actual.Status.Status)
+			}
+			for _, expected := range tt.expectedConnectorStatuses {
+				actual, err := clients.GetSkupperClient().SkupperV2alpha1().Connectors(expected.Namespace).Get(context.Background(), expected.Name, metav1.GetOptions{})
+				assert.Assert(t, err)
+				verifyStatus(t, expected.Status.Status, actual.Status.Status)
+			}
+			for _, expected := range tt.expectedAttachedConnectorStatuses {
+				actual, err := clients.GetSkupperClient().SkupperV2alpha1().AttachedConnectors(expected.Namespace).Get(context.Background(), expected.Name, metav1.GetOptions{})
+				assert.Assert(t, err)
+				verifyStatus(t, expected.Status.Status, actual.Status.Status)
+			}
+			for _, expected := range tt.expectedAttachedConnectorBindingStatuses {
+				actual, err := clients.GetSkupperClient().SkupperV2alpha1().AttachedConnectorBindings(expected.Namespace).Get(context.Background(), expected.Name, metav1.GetOptions{})
+				assert.Assert(t, err)
+				verifyStatus(t, expected.Status.Status, actual.Status.Status)
+			}
+			for resource, expected := range tt.expectedDynamicResources {
+				actual, err := clients.GetDynamicClient().Resource(resource).Namespace(expected.GetNamespace()).Get(context.Background(), expected.GetName(), metav1.GetOptions{})
+				assert.Assert(t, err)
+				actualLabels := actual.GetLabels()
+				for key, expectedValue := range expected.GetLabels() {
+					actualValue, ok := actualLabels[key]
+					assert.Assert(t, ok, key)
+					assert.Equal(t, expectedValue, actualValue)
+				}
+				match(expected.UnstructuredContent(), actual.UnstructuredContent())
+			}
+			for _, expected := range tt.expectedRouterConfig {
+				actual, err := clients.GetKubeClient().CoreV1().ConfigMaps(expected.namespace).Get(context.Background(), expected.name, metav1.GetOptions{})
+				assert.Assert(t, err)
+				assert.Assert(t, expected.verify(t, actual))
+			}
+			for _, expected := range tt.expectedServices {
+				actual, err := clients.GetKubeClient().CoreV1().Services(expected.Namespace).Get(context.Background(), expected.Name, metav1.GetOptions{})
+				assert.Assert(t, err)
+				assert.DeepEqual(t, expected.Spec.Selector, actual.Spec.Selector)
+				assert.Equal(t, len(expected.Spec.Ports), len(actual.Spec.Ports))
+				for i, port := range expected.Spec.Ports {
+					// in some cases it is not possible to know the order in which ports on the router are assigned, use * as a wildcard in such cases
+					if port.TargetPort.String() == "*" {
+						expected.Spec.Ports[i].TargetPort = actual.Spec.Ports[i].TargetPort
+					}
+				}
+				assert.DeepEqual(t, expected.Spec.Ports, actual.Spec.Ports)
+			}
+			for _, expected := range tt.expectedRouterAccesses {
+				actual, err := clients.GetSkupperClient().SkupperV2alpha1().RouterAccesses(expected.Namespace).Get(context.Background(), expected.Name, metav1.GetOptions{})
+				assert.Assert(t, err)
+				assert.DeepEqual(t, expected.Spec, actual.Spec)
+			}
+			for _, expected := range tt.expectedSecuredAccesses {
+				actual, err := clients.GetSkupperClient().SkupperV2alpha1().SecuredAccesses(expected.Namespace).Get(context.Background(), expected.Name, metav1.GetOptions{})
+				assert.Assert(t, err)
+				assert.DeepEqual(t, expected.Spec, actual.Spec)
+			}
+		})
+	}
+}
+
+func verifyStatus(t *testing.T, expected skupperv2alpha1.Status, actual skupperv2alpha1.Status) {
+	assert.Equal(t, expected.StatusType, actual.StatusType)
+	assert.Equal(t, expected.Message, actual.Message)
+	for _, condition := range expected.Conditions {
+		existing := meta.FindStatusCondition(actual.Conditions, condition.Type)
+		assert.Assert(t, existing != nil)
+		assert.Equal(t, condition.Status, existing.Status)
+		assert.Equal(t, condition.Reason, existing.Reason)
+		if condition.Message != "" {
+			assert.Equal(t, condition.Message, existing.Message)
+		}
+	}
+}
+
+func enableSSA(client dynamic.Interface) bool {
+	if fc, ok := client.(*fakedynamic.FakeDynamicClient); ok {
+		fc.PrependReactor(
+			"patch",
+			"*",
+			func(action k8stesting.Action) (bool, runtime.Object, error) {
+				pa := action.(k8stesting.PatchAction)
+				if pa.GetPatchType() != types.ApplyPatchType {
+					return false, nil, nil
+				}
+				// Apply patches are supposed to upsert, but fake client fails if the object doesn't exist,
+				// if an apply patch occurs for a deployment that doesn't yet exist, create it.
+				// However, we already hold the fakeclient lock, so we can't use the front door.
+				rfunc := k8stesting.ObjectReaction(fc.Tracker())
+				_, obj, err := rfunc(
+					k8stesting.NewGetAction(pa.GetResource(), pa.GetNamespace(), pa.GetName()),
+				)
+				if errors.IsNotFound(err) || obj == nil {
+					_, _, _ = rfunc(
+						k8stesting.NewCreateAction(
+							pa.GetResource(),
+							pa.GetNamespace(),
+							&appsv1.Deployment{
+								ObjectMeta: metav1.ObjectMeta{
+									Name:      pa.GetName(),
+									Namespace: pa.GetNamespace(),
+								},
+							},
+						),
+					)
+				}
+				return rfunc(k8stesting.NewPatchAction(
+					pa.GetResource(),
+					pa.GetNamespace(),
+					pa.GetName(),
+					types.StrategicMergePatchType,
+					pa.GetPatch()))
+			},
+		)
+		return true
+	}
+	return false
+}
+
+type factory struct{}
+
+func (*factory) routerConfig(name string, namespace string) *RouterConfig {
+	return &RouterConfig{
+		name:      name,
+		namespace: namespace,
+		config: &qdr.RouterConfig{
+			Addresses:   map[string]qdr.Address{},
+			SslProfiles: map[string]qdr.SslProfile{},
+			Listeners:   map[string]qdr.Listener{},
+			Connectors:  map[string]qdr.Connector{},
+			LogConfig:   map[string]qdr.LogConfig{},
+			Bridges: qdr.BridgeConfig{
+				TcpListeners:  map[string]qdr.TcpEndpoint{},
+				TcpConnectors: map[string]qdr.TcpEndpoint{},
+			},
+		},
+	}
+}
+
+func (*factory) site(name string, namespace string, linkAccess string, ha bool, edge bool) *skupperv2alpha1.Site {
+	return &skupperv2alpha1.Site{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "Site",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+		Spec: skupperv2alpha1.SiteSpec{
+			HA:         ha,
+			LinkAccess: linkAccess,
+			Edge:       edge,
+		},
+	}
+}
+
+func (*factory) listener(name string, namespace string, host string, port int) *skupperv2alpha1.Listener {
+	return &skupperv2alpha1.Listener{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "Listener",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+		Spec: skupperv2alpha1.ListenerSpec{
+			Host: host,
+			Port: port,
+		},
+	}
+}
+
+func (*factory) connector(name string, namespace string, host string, port int) *skupperv2alpha1.Connector {
+	return &skupperv2alpha1.Connector{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "Connector",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+		Spec: skupperv2alpha1.ConnectorSpec{
+			Host: host,
+			Port: port,
+		},
+	}
+}
+
+func (*factory) connectorWithSelector(name string, namespace string, selector string, port int) *skupperv2alpha1.Connector {
+	return &skupperv2alpha1.Connector{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "Connector",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+		Spec: skupperv2alpha1.ConnectorSpec{
+			Selector: selector,
+			Port:     port,
+		},
+	}
+}
+
+func (*factory) listenerWithExposePodsByName(name string, namespace string, key string, host string, port int) *skupperv2alpha1.Listener {
+	return &skupperv2alpha1.Listener{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "Listener",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+		Spec: skupperv2alpha1.ListenerSpec{
+			Host:             host,
+			Port:             port,
+			ExposePodsByName: true,
+			RoutingKey:       key,
+		},
+	}
+}
+
+func (*factory) connectorWithExposePodsByName(name string, namespace string, key string, selector string, port int) *skupperv2alpha1.Connector {
+	return &skupperv2alpha1.Connector{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "Connector",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+		Spec: skupperv2alpha1.ConnectorSpec{
+			Selector:         selector,
+			Port:             port,
+			ExposePodsByName: true,
+			RoutingKey:       key,
+		},
+	}
+}
+
+func (*factory) attachedConnector(name string, namespace string, siteNamespace string, selector string, port int) *skupperv2alpha1.AttachedConnector {
+	return &skupperv2alpha1.AttachedConnector{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "AttachedConnector",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+		Spec: skupperv2alpha1.AttachedConnectorSpec{
+			SiteNamespace: siteNamespace,
+			Selector:      selector,
+			Port:          port,
+		},
+	}
+}
+
+func (*factory) attachedConnectorBinding(name string, namespace string, connectorNamespace string) *skupperv2alpha1.AttachedConnectorBinding {
+	return &skupperv2alpha1.AttachedConnectorBinding{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "AttachedConnectorBinding",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+		Spec: skupperv2alpha1.AttachedConnectorBindingSpec{
+			ConnectorNamespace: connectorNamespace,
+		},
+	}
+}
+
+func (*factory) siteStatus(name string, namespace string, statusType skupperv2alpha1.StatusType, message string, conditions ...metav1.Condition) *skupperv2alpha1.Site {
+	return &skupperv2alpha1.Site{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "Site",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+		Status: skupperv2alpha1.SiteStatus{
+			Status: f.status(statusType, message, conditions...),
+		},
+	}
+}
+
+func (*factory) listenerStatus(name string, namespace string, statusType skupperv2alpha1.StatusType, message string, conditions ...metav1.Condition) *skupperv2alpha1.Listener {
+	return &skupperv2alpha1.Listener{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "Listener",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+		Status: skupperv2alpha1.ListenerStatus{
+			Status: f.status(statusType, message, conditions...),
+		},
+	}
+}
+
+func (*factory) connectorStatus(name string, namespace string, statusType skupperv2alpha1.StatusType, message string, conditions ...metav1.Condition) *skupperv2alpha1.Connector {
+	return &skupperv2alpha1.Connector{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "Connector",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+		Status: skupperv2alpha1.ConnectorStatus{
+			Status: f.status(statusType, message, conditions...),
+		},
+	}
+}
+
+func (*factory) attachedConnectorStatus(name string, namespace string, statusType skupperv2alpha1.StatusType, message string, conditions ...metav1.Condition) *skupperv2alpha1.AttachedConnector {
+	return &skupperv2alpha1.AttachedConnector{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "AttachedConnector",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+		Status: skupperv2alpha1.AttachedConnectorStatus{
+			Status: f.status(statusType, message, conditions...),
+		},
+	}
+}
+
+func (*factory) attachedConnectorBindingStatus(name string, namespace string, statusType skupperv2alpha1.StatusType, message string, conditions ...metav1.Condition) *skupperv2alpha1.AttachedConnectorBinding {
+	return &skupperv2alpha1.AttachedConnectorBinding{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "AttachedConnectorBinding",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+		Status: skupperv2alpha1.AttachedConnectorBindingStatus{
+			Status: f.status(statusType, message, conditions...),
+		},
+	}
+}
+
+func (*factory) status(statusType skupperv2alpha1.StatusType, message string, conditions ...metav1.Condition) skupperv2alpha1.Status {
+	return skupperv2alpha1.Status{
+		StatusType: statusType,
+		Message:    message,
+		Conditions: conditions,
+	}
+}
+
+func (*factory) condition(conditionType string, status metav1.ConditionStatus, reason string, message string) metav1.Condition {
+	return metav1.Condition{
+		Type:    conditionType,
+		Status:  status,
+		Reason:  reason,
+		Message: message,
+	}
+}
+
+func (*factory) routerSelector(application bool) map[string]string {
+	selector := map[string]string{
+		"skupper.io/component": "router",
+	}
+	if application {
+		selector["application"] = "skupper-router"
+	}
+	return selector
+}
+
+func (*factory) routerSelectorWithGroup(group string) map[string]string {
+	selector := map[string]string{
+		"skupper.io/component": "router",
+		"skupper.io/group":     group,
+	}
+	return selector
+}
+
+func (f *factory) routerDeployment(name string, namespace string) *unstructured.Unstructured {
+	labels := map[string]string{
+		"application":          "skupper-router",
+		"skupper.io/component": "router",
+		"skupper.io/type":      "site",
+	}
+	content := map[string]interface{}{
+		"spec": map[string]interface{}{
+			"selector": map[string]interface{}{
+				"matchLabels": f.routerSelector(false),
+			},
+			"template": map[string]interface{}{
+				"spec": map[string]interface{}{
+					"containers": []map[string]interface{}{
+						{
+							"name": "router",
+						},
+						{
+							"name": "kube-adaptor",
+						},
+					},
+				},
+			},
+		},
+	}
+	return f.unstructured(name, namespace, content, labels)
+}
+
+func (*factory) unstructured(name string, namespace string, content map[string]interface{}, labels map[string]string) *unstructured.Unstructured {
+	obj := &unstructured.Unstructured{}
+	if content != nil {
+		obj.SetUnstructuredContent(content)
+	}
+	obj.SetName(name)
+	obj.SetNamespace(namespace)
+	if labels != nil {
+		obj.SetLabels(labels)
+	}
+	return obj
+}
+
+func (*factory) routerAccess(name string, namespace string, accessType string, credentials string, generate bool, issuer string, roles ...skupperv2alpha1.RouterAccessRole) *skupperv2alpha1.RouterAccess {
+	return &skupperv2alpha1.RouterAccess{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "RouterAccess",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+		Spec: skupperv2alpha1.RouterAccessSpec{
+			AccessType:             accessType,
+			TlsCredentials:         credentials,
+			GenerateTlsCredentials: generate,
+			Issuer:                 issuer,
+			Roles:                  roles,
+		},
+	}
+}
+
+func (*factory) role(name string, port int) skupperv2alpha1.RouterAccessRole {
+	return skupperv2alpha1.RouterAccessRole{
+		Name: name,
+		Port: port,
+	}
+}
+
+func (*factory) securedAccess(name string, namespace string, accessType string, selector map[string]string, certificate string, issuer string, ports ...skupperv2alpha1.SecuredAccessPort) *skupperv2alpha1.SecuredAccess {
+	return &skupperv2alpha1.SecuredAccess{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "skupper.io/v2alpha1",
+			Kind:       "SecuredAccess",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+		Spec: skupperv2alpha1.SecuredAccessSpec{
+			AccessType:  accessType,
+			Selector:    selector,
+			Certificate: certificate,
+			Issuer:      issuer,
+			Ports:       ports,
+		},
+	}
+}
+
+func (*factory) securedAccessPort(name string, port int) skupperv2alpha1.SecuredAccessPort {
+	return skupperv2alpha1.SecuredAccessPort{
+		Name:       name,
+		Port:       port,
+		TargetPort: port,
+		Protocol:   "TCP",
+	}
+}
+
+func (*factory) service(name string, namespace string, selector map[string]string, ports ...corev1.ServicePort) *corev1.Service {
+	return &corev1.Service{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+		Spec: corev1.ServiceSpec{
+			Selector: selector,
+			Ports:    ports,
+		},
+	}
+}
+
+func (*factory) servicePort(name string, port int32, targetPort int32) corev1.ServicePort {
+	return corev1.ServicePort{
+		Name:       name,
+		Port:       port,
+		TargetPort: intstr.IntOrString{IntVal: int32(targetPort)},
+		Protocol:   corev1.Protocol("TCP"),
+	}
+}
+
+// where the target port cannot be determined in advance, use * as wildcard
+func (*factory) servicePortU(name string, port int32) corev1.ServicePort {
+	return corev1.ServicePort{
+		Name:       name,
+		Port:       port,
+		TargetPort: intstr.FromString("*"),
+		Protocol:   corev1.Protocol("TCP"),
+	}
+}
+
+func (*factory) pod(name string, namespace string, labels map[string]string, ownerRefs []metav1.OwnerReference, status *corev1.PodStatus) *corev1.Pod {
+	pod := &corev1.Pod{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "v1",
+			Kind:       "Pod",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:            name,
+			Namespace:       namespace,
+			Labels:          labels,
+			OwnerReferences: ownerRefs,
+		},
+	}
+	if status != nil {
+		pod.Status = *status
+	}
+	return pod
+}
+
+func (*factory) podStatus(ip string, phase corev1.PodPhase, conditions ...corev1.PodCondition) *corev1.PodStatus {
+	return &corev1.PodStatus{
+		Phase:      phase,
+		Conditions: conditions,
+		PodIP:      ip,
+	}
+}
+
+func (*factory) podCondition(conditionType corev1.PodConditionType, status corev1.ConditionStatus) corev1.PodCondition {
+	return corev1.PodCondition{
+		Type:   conditionType,
+		Status: status,
+	}
+}
+
+func (*factory) addNetworkStatus(site *skupperv2alpha1.Site, status *NetworkStatus) *skupperv2alpha1.Site {
+	//add equivalent data to that generated in networkStatusInfo
+	site.Status.Network = extractSiteRecords(*status.info())
+	return site
+}
+
+func (*factory) skupperNetworkStatus(namespace string, status *network.NetworkStatusInfo) *corev1.ConfigMap {
+	data, _ := json.Marshal(status)
+	return &corev1.ConfigMap{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "skupper-network-status",
+			Namespace: namespace,
+		},
+		Data: map[string]string{
+			"NetworkStatus": string(data),
+		},
+	}
+}
+
+func (f *factory) fakeNetworkStatusInfo(namespace string) *NetworkStatus {
+	return f.networkStatusInfo(
+		"east", namespace, map[string]string{"mysvc": "mysvc"}, map[string]string{"mysvc": "foo"},
+	).site(
+		"west", "other", nil, nil,
+	).link(
+		"west", "east",
+	)
+}
+
+type NetworkStatus struct {
+	sites map[string]*network.SiteStatusInfo
+}
+
+func (n *NetworkStatus) info() *network.NetworkStatusInfo {
+	info := &network.NetworkStatusInfo{}
+	for _, site := range n.sites {
+		info.SiteStatus = append(info.SiteStatus, *site)
+	}
+	return info
+}
+
+func (n *NetworkStatus) site(name string, namespace string, listeners map[string]string, connectors map[string]string) *NetworkStatus {
+	router := network.RouterStatusInfo{
+		Router: network.RouterInfo{
+			Name:      name + "-skupper-router-" + rand.String(10) + "-" + rand.String(5),
+			Namespace: namespace,
+		},
+		AccessPoints: []network.RouterAccessInfo{
+			{
+				Identity: uuid.NewString(),
+			},
+		},
+	}
+	for address, name := range listeners {
+		router.Listeners = append(router.Listeners, network.ListenerInfo{
+			Name:    name,
+			Address: address,
+		})
+	}
+	for address, host := range connectors {
+		router.Connectors = append(router.Connectors, network.ConnectorInfo{
+			Address:  address,
+			DestHost: host,
+		})
+
+	}
+
+	n.sites[name] = &network.SiteStatusInfo{
+		Site: network.SiteInfo{
+			Identity:  uuid.NewString(),
+			Name:      name,
+			Namespace: namespace,
+		},
+		RouterStatus: []network.RouterStatusInfo{
+			router,
+		},
+	}
+	return n
+}
+
+func (n *NetworkStatus) link(from string, to string) *NetworkStatus {
+	siteA, ok1 := n.sites[from]
+	siteB, ok2 := n.sites[to]
+	if ok1 && ok2 && len(siteA.RouterStatus[0].AccessPoints) > 0 {
+		link := network.LinkInfo{
+			Name: fmt.Sprintf("link-%d", len(siteA.RouterStatus[0].Links)+1),
+			Peer: siteB.RouterStatus[0].AccessPoints[0].Identity,
+		}
+		siteA.RouterStatus[0].Links = append(siteA.RouterStatus[0].Links, link)
+	}
+	return n
+}
+
+func (*factory) networkStatusInfo(name string, namespace string, listeners map[string]string, connectors map[string]string) *NetworkStatus {
+	n := &NetworkStatus{
+		sites: map[string]*network.SiteStatusInfo{},
+	}
+	return n.site(name, namespace, listeners, connectors)
+}
+
+var f factory
+
+// ensures all fields specified in expected are in actual
+func match(expected interface{}, actual interface{}) error {
+	return _match(reflect.ValueOf(expected), reflect.ValueOf(actual))
+}
+
+func _match(expected reflect.Value, actual reflect.Value) error {
+	if expected.Kind() != actual.Kind() {
+		return fmt.Errorf("Expected value of type %s, got value of type %s", expected.Kind(), actual.Kind())
+	} else if expected.Kind() == reflect.Map {
+		for _, key := range expected.MapKeys() {
+			if err := _match(expected.MapIndex(key), actual.MapIndex(key)); err != nil {
+				return fmt.Errorf("Value for %s does not match: %s", key.String(), err)
+			}
+		}
+		return nil
+	} else if expected.Kind() == reflect.Slice || expected.Kind() == reflect.Array {
+		if expected.Len() != actual.Len() {
+			return fmt.Errorf("Different number of items, expected %s, got %s", expected.String(), actual.String())
+		}
+		for i := 0; i < expected.Len(); i++ {
+			if err := _match(expected.Index(i), actual.Index(i)); err != nil {
+				return fmt.Errorf("Item at index %d does not match: %s", i, err)
+			}
+		}
+		return nil
+	} else if expected.Comparable() {
+		if !expected.Equal(actual) {
+			return fmt.Errorf("Expected %s, got  %s", expected.String(), actual.String())
+		}
+		return nil
+	} else {
+		return fmt.Errorf("Type %s is not comparable", expected.Kind())
+	}
+}
+
+type RouterConfig struct {
+	namespace string
+	name      string
+	config    *qdr.RouterConfig
+}
+
+func (rc *RouterConfig) tcpListener(name string, port string, address string, sslProfile string) *RouterConfig {
+	rc.config.Bridges.TcpListeners[name] = qdr.TcpEndpoint{
+		Name:       name,
+		Port:       port,
+		Address:    address,
+		SslProfile: sslProfile,
+	}
+	return rc
+}
+
+func (rc *RouterConfig) tcpConnector(name string, host string, port string, address string, sslProfile string) *RouterConfig {
+	rc.config.Bridges.TcpConnectors[name] = qdr.TcpEndpoint{
+		Name:       name,
+		Host:       host,
+		Port:       port,
+		Address:    address,
+		SslProfile: sslProfile,
+	}
+	return rc
+}
+
+func (rc *RouterConfig) verify(t *testing.T, cm *corev1.ConfigMap) error {
+	config, err := qdr.GetRouterConfigFromConfigMap(cm)
+	assert.Assert(t, err)
+	for key, expected := range rc.config.Addresses {
+		actual, ok := config.Addresses[key]
+		assert.Assert(t, ok, "No address found for %s", key)
+		assert.Equal(t, actual, expected)
+	}
+	for key, expected := range rc.config.SslProfiles {
+		actual, ok := config.SslProfiles[key]
+		assert.Assert(t, ok, "No ssl profile found for %s", key)
+		assert.Equal(t, actual, expected)
+	}
+	for key, expected := range rc.config.Listeners {
+		actual, ok := config.Listeners[key]
+		assert.Assert(t, ok, "No listener found for %s", key)
+		assert.Equal(t, actual, expected)
+	}
+	for key, expected := range rc.config.Connectors {
+		actual, ok := config.Connectors[key]
+		assert.Assert(t, ok, "No connector found for %s", key)
+		assert.Equal(t, actual, expected)
+	}
+	for key, expected := range rc.config.Bridges.TcpListeners {
+		actual, ok := config.Bridges.TcpListeners[key]
+		assert.Assert(t, ok, "No tcp listener found for %s", key)
+		if expected.Port == "*" {
+			// in some cases it is not possible to deterministically infer the order in which ports will be assigned
+			expected.Port = actual.Port
+		}
+		assert.Equal(t, actual, expected)
+	}
+	for key, expected := range rc.config.Bridges.TcpConnectors {
+		actual, ok := config.Bridges.TcpConnectors[key]
+		assert.Assert(t, ok, "No tcp connector found for %s", key)
+		assert.Equal(t, actual, expected)
+	}
+	// for bridges, if specify at least one, specify them all:
+	if len(rc.config.Bridges.TcpListeners) > 0 {
+		assert.Equal(t, len(rc.config.Bridges.TcpListeners), len(config.Bridges.TcpListeners))
+	}
+	if len(rc.config.Bridges.TcpConnectors) > 0 {
+		assert.Equal(t, len(rc.config.Bridges.TcpConnectors), len(config.Bridges.TcpConnectors))
+	}
+	return nil
+}
+
+func isSiteStatusConditionTrue(name string, namespace string, condition string) WaitFunction {
+	return func(t *testing.T, clients internalclient.Clients) bool {
+		site, err := clients.GetSkupperClient().SkupperV2alpha1().Sites(namespace).Get(context.Background(), name, metav1.GetOptions{})
+		assert.Assert(t, err)
+		return meta.IsStatusConditionTrue(site.Status.Conditions, condition)
+	}
+}
+
+func isSiteNetworkStatusSet(name string, namespace string) WaitFunction {
+	return func(t *testing.T, clients internalclient.Clients) bool {
+		site, err := clients.GetSkupperClient().SkupperV2alpha1().Sites(namespace).Get(context.Background(), name, metav1.GetOptions{})
+		assert.Assert(t, err)
+		return site.Status.Network != nil
+	}
+}
+
+func isConnectorStatusConditionTrue(name string, namespace string, condition string) WaitFunction {
+	return func(t *testing.T, clients internalclient.Clients) bool {
+		connector, err := clients.GetSkupperClient().SkupperV2alpha1().Connectors(namespace).Get(context.Background(), name, metav1.GetOptions{})
+		assert.Assert(t, err)
+		return meta.IsStatusConditionTrue(connector.Status.Conditions, condition)
+	}
+}
+
+func isListenerStatusConditionTrue(name string, namespace string, condition string) WaitFunction {
+	return func(t *testing.T, clients internalclient.Clients) bool {
+		listener, err := clients.GetSkupperClient().SkupperV2alpha1().Listeners(namespace).Get(context.Background(), name, metav1.GetOptions{})
+		assert.Assert(t, err)
+		return meta.IsStatusConditionTrue(listener.Status.Conditions, condition)
+	}
+}
+
+func isAttachedConnectorStatusConditionTrue(name string, namespace string, condition string) WaitFunction {
+	return func(t *testing.T, clients internalclient.Clients) bool {
+		connector, err := clients.GetSkupperClient().SkupperV2alpha1().AttachedConnectors(namespace).Get(context.Background(), name, metav1.GetOptions{})
+		assert.Assert(t, err)
+		return meta.IsStatusConditionTrue(connector.Status.Conditions, condition)
+	}
+}
diff --git a/pkg/nonkube/common/systemd_test.go b/pkg/nonkube/common/systemd_test.go
index e3d0345f..65c75d77 100644
--- a/pkg/nonkube/common/systemd_test.go
+++ b/pkg/nonkube/common/systemd_test.go
@@ -29,7 +29,7 @@ func TestSystemdService(t *testing.T) {
 	t.Setenv("SKUPPER_OUTPUT_PATH", outputPath)
 	t.Setenv("XDG_CONFIG_HOME", outputPath)
 
-	for _, platform := range []string{"systemd", "podman", "docker"} {
+	for _, platform := range []string{"linux", "podman", "docker"} {
 		for _, uid := range []int{0, 1000} {
 			//assert.Assert(t, t.Setenv("SKUPPER_PLATFORM", platform))
 			systemdService, err := NewSystemdServiceInfo(siteState, platform)
@@ -54,7 +54,7 @@ func TestSystemdService(t *testing.T) {
 				var startCmd string
 				var stopCmd string
 				switch platform {
-				case "systemd":
+				case "linux":
 					startCmd = fmt.Sprintf("ExecStart=skrouterd -c %s/skrouterd.json", systemdServiceImpl.SiteConfigPath)
 					stopCmd = ""
 					assert.Assert(t, strings.Contains(string(serviceFile), `Environment="SKUPPER_SITE_ID=site-id"`), string(serviceFile))
diff --git a/pkg/utils/retry_test.go b/pkg/utils/retry_test.go
index 38c7ca43..8ce2426b 100644
--- a/pkg/utils/retry_test.go
+++ b/pkg/utils/retry_test.go
@@ -390,8 +390,12 @@ func TestRetryErrorWithContext(t *testing.T) {
 					t.Errorf("Expected to complete before timeout, but took %v", elapsed)
 				}
 			}
-			if !item.expectSuccess && (elapsed <= item.timeout || elapsed > item.timeout+20*time.Millisecond) {
-				t.Errorf("The execution should have timed out, but it did not or took too long.")
+			if !item.expectSuccess {
+				if elapsed <= item.timeout {
+					t.Errorf("The execution should have timed out, but it did not.")
+				} else if elapsed > item.timeout+20*time.Millisecond {
+					t.Errorf("The execution should have timed out, but it took too long. Elapsed %d ms, timeout %d ms.", elapsed/time.Millisecond, item.timeout)
+				}
 			}
 
 			if item.expectSuccess != (resp == nil) {