Repository: skupperproject/skupper
Pull request #1784 (2024-11-14 01:12:51)

CollectorAPI: implement sort by for boolean fields
image build: `quay.io/ckruse/skupper/skupper/network-console-collector:collector-api-sortByBool`


Patch:
diff --git a/cmd/config-sync/main.go b/cmd/config-sync/main.go
index a9541c7b..3510a763 100644
--- a/cmd/config-sync/main.go
+++ b/cmd/config-sync/main.go
@@ -12,6 +12,8 @@ import (
 
 	corev1 "k8s.io/api/core/v1"
 
+	iflag "github.com/skupperproject/skupper/internal/flag"
+	"github.com/skupperproject/skupper/internal/kube/adaptor"
 	internalclient "github.com/skupperproject/skupper/internal/kube/client"
 	"github.com/skupperproject/skupper/pkg/kube"
 	"github.com/skupperproject/skupper/pkg/version"
@@ -36,12 +38,23 @@ func SetupSignalHandler() (stopCh <-chan struct{}) {
 	return stop
 }
 
-const SHARED_TLS_DIRECTORY = "/etc/skupper-router-certs"
-
 func main() {
+	flags := flag.NewFlagSet("", flag.ExitOnError)
+
+	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 configDir string
+	var configMapName string
+	iflag.StringVar(flags, &configDir, "config-dir", "SKUPPER_CONFIG_DIR", "/etc/skupper-router-certs", "The directory to which configuration should be saved")
+	iflag.StringVar(flags, &configMapName, "router-config", "SKUPPER_ROUTER_CONFIG", "skupper-router", "The name of the ConfigMap containg the router config")
+
 	// if -version used, report and exit
-	isVersion := flag.Bool("version", false, "Report the version of Config Sync")
-	flag.Parse()
+	isVersion := flags.Bool("version", false, "Report the version of Config Sync")
+	isInit := flags.Bool("init", false, "Downloads configuration and ssl profile artefacts")
+	flags.Parse(os.Args[1:])
 	if *isVersion {
 		fmt.Println(version.Version)
 		os.Exit(0)
@@ -53,11 +66,18 @@ func main() {
 	// set up signals so we handle the first shutdown signal gracefully
 	stopCh := SetupSignalHandler()
 
-	cli, err := internalclient.NewClient("", "", "")
+	cli, err := internalclient.NewClient(namespace, "", kubeconfig)
 	if err != nil {
 		log.Fatal("Error getting van client: ", err.Error())
 	}
 
+	if *isInit {
+		if err := adaptor.InitialiseConfig(cli.GetKubeClient(), cli.GetNamespace(), configDir, configMapName); err != nil {
+			log.Fatal("Error initialising config ", err.Error())
+		}
+		os.Exit(0)
+	}
+
 	log.Println("CONFIG_SYNC: Waiting for Skupper router to be ready")
 	_, err = kube.WaitForPodsSelectorStatus(cli.GetNamespace(), cli.Kube, "skupper.io/component=router", corev1.PodRunning, time.Second*180, time.Second*5)
 	if err != nil {
@@ -65,7 +85,7 @@ func main() {
 	}
 
 	log.Println("CONFIG_SYNC: Starting collector...")
-	go startCollector(cli)
+	go adaptor.StartCollector(cli)
 
 	//start health check
 	http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
@@ -74,16 +94,11 @@ func main() {
 	})
 	go http.ListenAndServe(":9191", nil)
 
-	routerConfigMap := os.Getenv("SKUPPER_CONFIG")
-	if routerConfigMap == "" {
-		routerConfigMap = "skupper-internal" // change defult?
-	}
-
-	configSync := newConfigSync(cli, cli.GetNamespace(), SHARED_TLS_DIRECTORY, routerConfigMap)
+	configSync := adaptor.NewConfigSync(cli, cli.GetNamespace(), configDir, configMapName)
 	log.Println("CONFIG_SYNC: Starting controller loop...")
-	configSync.start(stopCh)
+	configSync.Start(stopCh)
 
 	<-stopCh
 	log.Println("CONFIG_SYNC: Shutting down...")
-	configSync.stop()
+	configSync.Stop()
 }
diff --git a/cmd/network-console-collector/internal/collector/addresses.go b/cmd/network-console-collector/internal/collector/addresses.go
index 9e6f20d1..c3372469 100644
--- a/cmd/network-console-collector/internal/collector/addresses.go
+++ b/cmd/network-console-collector/internal/collector/addresses.go
@@ -11,22 +11,24 @@ import (
 )
 
 type addressManager struct {
-	logger          *slog.Logger
-	stor            store.Interface
-	mu              sync.Mutex
-	idp             idProvider
-	addresses       map[string]struct{}
-	updateAddresses chan AddressRecord
-	source          store.SourceRef
+	logger              *slog.Logger
+	stor                store.Interface
+	mu                  sync.Mutex
+	idp                 idProvider
+	addresses           map[string]struct{}
+	updateAddresses     chan AddressRecord
+	maybePurgeAddresses chan AddressRecord
+	source              store.SourceRef
 }
 
 func newAddressManager(logger *slog.Logger, stor store.Interface) *addressManager {
 	return &addressManager{
-		logger:          logger,
-		stor:            stor,
-		addresses:       make(map[string]struct{}),
-		updateAddresses: make(chan AddressRecord, 16),
-		idp:             newStableIdentityProvider(),
+		logger:              logger,
+		stor:                stor,
+		addresses:           make(map[string]struct{}),
+		updateAddresses:     make(chan AddressRecord, 16),
+		maybePurgeAddresses: make(chan AddressRecord, 16),
+		idp:                 newStableIdentityProvider(),
 		source: store.SourceRef{
 			Version: "0.1",
 			ID:      "self",
@@ -54,56 +56,93 @@ func (m *addressManager) run(ctx context.Context) func() error {
 						slog.String("name", addr.Name),
 					)
 				}()
+			case addr := <-m.maybePurgeAddresses:
+				func() {
+					m.mu.Lock()
+					defer m.mu.Unlock()
+					if _, ok := m.addresses[addr.ID]; !ok {
+						return
+					}
+					addressName := addr.Name
+					entries := m.stor.Index(IndexByAddress, store.Entry{Record: vanflow.ConnectorRecord{Address: &addressName}})
+					if len(entries) > 0 {
+						return
+					}
+					m.stor.Delete(addr.ID)
+					delete(m.addresses, addr.ID)
+					m.logger.Info("Deleted address record",
+						slog.String("id", addr.ID),
+						slog.String("name", addr.Name),
+					)
+				}()
 			}
 		}
 	}
 }
 
 func (m *addressManager) handleChangeEvent(event changeEvent, stor readonly) {
-	if _, ok := event.(deleteEvent); ok {
+	if delEvent, ok := event.(deleteEvent); ok {
+		address, ok := m.addressFromRecord(delEvent.Record)
+		if !ok {
+			return
+		}
+		if !m.hasAddress(address) {
+			return
+		}
+		m.maybePurgeAddresses <- address
 		return
 	}
 	entry, ok := stor.Get(event.ID())
 	if !ok {
 		return
 	}
-	var (
-		address   string
-		startTime time.Time
-		protocol  string = "tcp"
-	)
-	switch r := entry.Record.(type) {
+	address, ok := m.addressFromRecord(entry.Record)
+	if !ok {
+		return // incomplete
+	}
+	if !m.hasAddress(address) {
+		m.updateAddresses <- address
+	}
+}
+
+func (m *addressManager) hasAddress(address AddressRecord) bool {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	addressID := m.idp.ID("adr", address.Name, address.Protocol)
+	_, ok := m.addresses[addressID]
+	return ok
+}
+
+func (m *addressManager) addressFromRecord(record vanflow.Record) (AddressRecord, bool) {
+	var addr AddressRecord
+	addr.Start = time.Now()
+	switch r := record.(type) {
 	case vanflow.ListenerRecord:
 		if r.Address != nil {
-			address = *r.Address
+			addr.Name = *r.Address
 		}
 		if r.StartTime != nil {
-			startTime = r.StartTime.Time
+			addr.Start = r.StartTime.Time
 		}
 		if r.Protocol != nil {
-			protocol = *r.Protocol
+			addr.Protocol = *r.Protocol
 		}
 	case vanflow.ConnectorRecord:
 		if r.Address != nil {
-			address = *r.Address
+			addr.Name = *r.Address
 		}
 		if r.StartTime != nil {
-			startTime = r.StartTime.Time
+			addr.Start = r.StartTime.Time
 		}
 		if r.Protocol != nil {
-			protocol = *r.Protocol
+			addr.Protocol = *r.Protocol
 		}
 	default:
+		return addr, false
 	}
-	if address == "" {
-		return
-	}
-
-	m.mu.Lock()
-	defer m.mu.Unlock()
-	addressID := m.idp.ID("adr", address, protocol)
-	if _, ok := m.addresses[addressID]; ok {
-		return
+	if addr.Name == "" || addr.Protocol == "" {
+		return addr, false
 	}
-	m.updateAddresses <- AddressRecord{ID: addressID, Name: address, Protocol: protocol, Start: startTime}
+	addr.ID = m.idp.ID("adr", addr.Name, addr.Protocol)
+	return addr, true
 }
diff --git a/cmd/network-console-collector/internal/server/query.go b/cmd/network-console-collector/internal/server/query.go
index 8ae7f309..ea352b8a 100644
--- a/cmd/network-console-collector/internal/server/query.go
+++ b/cmd/network-console-collector/internal/server/query.go
@@ -181,6 +181,16 @@ func (m fieldIndex[T]) Compare(x, y T) int {
 	case reflect.Int64:
 		xx, yy := vx.Int(), vy.Int()
 		return int(xx - yy)
+	case reflect.Bool:
+		xx, yy := vx.Bool(), vy.Bool()
+		switch {
+		case xx == yy:
+			return 0
+		case xx && !yy:
+			return 1
+		default:
+			return -1
+		}
 	}
 	return 0
 }
diff --git a/internal/cmd/skupper/connector/nonkube/connector_status.go b/internal/cmd/skupper/connector/nonkube/connector_status.go
index 7ea8f487..2ad5f1d8 100644
--- a/internal/cmd/skupper/connector/nonkube/connector_status.go
+++ b/internal/cmd/skupper/connector/nonkube/connector_status.go
@@ -35,6 +35,7 @@ func (cmd *CmdConnectorStatus) NewClient(cobraCommand *cobra.Command, args []str
 
 func (cmd *CmdConnectorStatus) ValidateInput(args []string) []error {
 	var validationErrors []error
+	opts := fs.GetOptions{RuntimeFirst: true}
 	resourceStringValidator := validator.NewResourceStringValidator()
 	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
 
@@ -55,7 +56,7 @@ func (cmd *CmdConnectorStatus) ValidateInput(args []string) []error {
 	}
 	// Validate that there is a connector with this name in the namespace
 	if cmd.connectorName != "" {
-		connector, err := cmd.connectorHandler.Get(cmd.connectorName)
+		connector, err := cmd.connectorHandler.Get(cmd.connectorName, opts)
 		if connector == nil || err != nil {
 			validationErrors = append(validationErrors, fmt.Errorf("connector %s does not exist in namespace %s", cmd.connectorName, cmd.namespace))
 		}
@@ -74,6 +75,7 @@ func (cmd *CmdConnectorStatus) ValidateInput(args []string) []error {
 }
 
 func (cmd *CmdConnectorStatus) Run() error {
+	opts := fs.GetOptions{RuntimeFirst: true}
 	if cmd.connectorName == "" {
 		connectors, err := cmd.connectorHandler.List()
 		if connectors == nil || err != nil {
@@ -103,7 +105,7 @@ func (cmd *CmdConnectorStatus) Run() error {
 			_ = tw.Flush()
 		}
 	} else {
-		connector, err := cmd.connectorHandler.Get(cmd.connectorName)
+		connector, err := cmd.connectorHandler.Get(cmd.connectorName, opts)
 		if connector == nil || err != nil {
 			fmt.Println("No connectors found:")
 			return err
diff --git a/internal/cmd/skupper/connector/nonkube/connector_update.go b/internal/cmd/skupper/connector/nonkube/connector_update.go
index e6044b63..5358950d 100644
--- a/internal/cmd/skupper/connector/nonkube/connector_update.go
+++ b/internal/cmd/skupper/connector/nonkube/connector_update.go
@@ -44,6 +44,7 @@ func (cmd *CmdConnectorUpdate) NewClient(cobraCommand *cobra.Command, args []str
 
 func (cmd *CmdConnectorUpdate) ValidateInput(args []string) []error {
 	var validationErrors []error
+	opts := fs.GetOptions{RuntimeFirst: false}
 	resourceStringValidator := validator.NewResourceStringValidator()
 	numberValidator := validator.NewNumberValidator()
 	connectorTypeValidator := validator.NewOptionValidator(common.ConnectorTypes)
@@ -76,7 +77,7 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) []error {
 
 	// Validate that there is already a connector with this name in the namespace
 	if cmd.connectorName != "" {
-		connector, err := cmd.connectorHandler.Update(cmd.connectorName)
+		connector, err := cmd.connectorHandler.Get(cmd.connectorName, opts)
 		if connector == nil || err != nil {
 			validationErrors = append(validationErrors, fmt.Errorf("connector %s must exist in namespace %s to be updated", cmd.connectorName, cmd.namespace))
 		} else {
diff --git a/cmd/config-sync/collector.go b/internal/kube/adaptor/collector.go
similarity index 98%
rename from cmd/config-sync/collector.go
rename to internal/kube/adaptor/collector.go
index 028cfba3..842fa9d1 100644
--- a/cmd/config-sync/collector.go
+++ b/internal/kube/adaptor/collector.go
@@ -1,4 +1,4 @@
-package main
+package adaptor
 
 import (
 	"context"
@@ -142,7 +142,7 @@ func runLeaderElection(lock *resourcelock.LeaseLock, id string, cli *internalcli
 	})
 }
 
-func startCollector(cli *internalclient.KubeClient) {
+func StartCollector(cli *internalclient.KubeClient) {
 	lockname := types.SiteLeaderLockName
 	namespace := cli.Namespace
 	podname, _ := os.Hostname()
diff --git a/internal/kube/adaptor/config_init.go b/internal/kube/adaptor/config_init.go
new file mode 100644
index 00000000..53be7ee7
--- /dev/null
+++ b/internal/kube/adaptor/config_init.go
@@ -0,0 +1,51 @@
+package adaptor
+
+import (
+	"context"
+	"log"
+	"os"
+	paths "path"
+
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/client-go/kubernetes"
+
+	"github.com/skupperproject/skupper/pkg/qdr"
+)
+
+func InitialiseConfig(client kubernetes.Interface, namespace string, path string, routerConfigMap string) error {
+	ctxt := context.Background()
+	current, err := client.CoreV1().ConfigMaps(namespace).Get(ctxt, routerConfigMap, metav1.GetOptions{})
+	if err != nil {
+		return err
+	}
+
+	config, err := qdr.GetRouterConfigFromConfigMap(current)
+	if err != nil {
+		return err
+	}
+
+	value, err := qdr.MarshalRouterConfig(*config)
+	if err != nil {
+		return err
+	}
+	configFile := paths.Join(path, "skrouterd.json")
+	if err := os.WriteFile(configFile, []byte(value), 0777); err != nil {
+		return err
+	}
+	log.Printf("Router configuration written to %s", configFile)
+
+	profileSyncer := newSslProfileSyncer(path)
+	for _, profile := range config.SslProfiles {
+		target, _ := profileSyncer.get(profile.Name)
+
+		secret, err := client.CoreV1().Secrets(namespace).Get(ctxt, target.name, metav1.GetOptions{})
+		if err != nil {
+			return err
+		}
+		if err := target.sync(secret); err != nil {
+			return err
+		}
+		log.Printf("Resources for SslProfile %s written to %s", profile.Name, target.path)
+	}
+	return nil
+}
diff --git a/cmd/config-sync/config_sync.go b/internal/kube/adaptor/config_sync.go
similarity index 75%
rename from cmd/config-sync/config_sync.go
rename to internal/kube/adaptor/config_sync.go
index 009115e1..f9917c05 100644
--- a/cmd/config-sync/config_sync.go
+++ b/internal/kube/adaptor/config_sync.go
@@ -1,12 +1,9 @@
-package main
+package adaptor
 
 import (
 	"fmt"
 	"log"
-	"os"
-	paths "path"
 	"reflect"
-	"strings"
 
 	corev1 "k8s.io/api/core/v1"
 
@@ -21,26 +18,26 @@ type ConfigSync struct {
 	agentPool       *qdr.AgentPool
 	controller      *kube.Controller
 	namespace       string
-	tracking        map[string]*SyncTarget
+	profileSyncer   *SslProfileSyncer
 	config          *kube.ConfigMapWatcher
 	secrets         *kube.SecretWatcher
 	path            string
 	routerConfigMap string
 }
 
-func newConfigSync(cli internalclient.Clients, namespace string, path string, routerConfigMap string) *ConfigSync {
+func NewConfigSync(cli internalclient.Clients, namespace string, path string, routerConfigMap string) *ConfigSync {
 	configSync := &ConfigSync{
 		agentPool:       qdr.NewAgentPool("amqp://localhost:5672", nil),
 		controller:      kube.NewController("config-sync", cli),
 		namespace:       namespace,
-		tracking:        map[string]*SyncTarget{},
+		profileSyncer:   newSslProfileSyncer(path),
 		path:            path,
 		routerConfigMap: routerConfigMap,
 	}
 	return configSync
 }
 
-func (c *ConfigSync) start(stopCh <-chan struct{}) error {
+func (c *ConfigSync) Start(stopCh <-chan struct{}) error {
 	if err := mkdir(c.path); err != nil {
 		return err
 	}
@@ -58,7 +55,7 @@ func (c *ConfigSync) start(stopCh <-chan struct{}) error {
 	return nil
 }
 
-func (c *ConfigSync) stop() {
+func (c *ConfigSync) Stop() {
 	c.controller.Stop()
 }
 
@@ -66,30 +63,11 @@ func (c *ConfigSync) key(name string) string {
 	return fmt.Sprintf("%s/%s", c.namespace, name)
 }
 
-func (c *ConfigSync) track(name string, path string) (*SyncTarget, bool) {
-	if current, ok := c.tracking[name]; ok {
-		log.Printf("CONFIG_SYNC: Secret %q already being tracked", name)
-		return current, false
-	} else {
-		target := &SyncTarget{
-			name: name,
-			path: path,
-		}
-		c.tracking[name] = target
-		log.Printf("CONFIG_SYNC: Tracking secret %q", name)
-		return target, true
-	}
-}
-
-func (c *ConfigSync) trackSslProfile(profile string) (*SyncTarget, bool) {
-	secret := profile
-	if strings.HasSuffix(profile, "-profile") {
-		secret = strings.TrimSuffix(profile, "-profile")
-	}
-	return c.track(secret, paths.Join(c.path, profile))
+func (c *ConfigSync) trackSslProfile(profile string) (*SslProfile, bool) {
+	return c.profileSyncer.get(profile)
 }
 
-func (c *ConfigSync) sync(target *SyncTarget) error {
+func (c *ConfigSync) sync(target *SslProfile) error {
 	secret, err := c.secrets.Get(c.key(target.name))
 	if err != nil {
 		return fmt.Errorf("CONFIG_SYNC: Error looking up secret for %s: %s", target.name, err)
@@ -110,7 +88,7 @@ func (c *ConfigSync) secretEvent(key string, secret *corev1.Secret) error {
 	if secret == nil {
 		return nil
 	}
-	if current, ok := c.tracking[secret.Name]; ok {
+	if current, ok := c.profileSyncer.bySecretName(secret.Name); ok {
 		if current.secret != nil && reflect.DeepEqual(current.secret.Data, secret.Data) {
 			log.Printf("CONFIG_SYNC: Secret %q already up to date", secret.Name)
 			return nil
@@ -270,9 +248,6 @@ func (c *ConfigSync) syncSslProfilesToRouter(desired map[string]qdr.SslProfile)
 
 func (c *ConfigSync) syncSslProfileCredentialsToDisk(profiles map[string]qdr.SslProfile) error {
 	for _, profile := range profiles {
-		if isExcludedProfile(profile.Name) {
-			continue
-		}
 		if tracker, sync := c.trackSslProfile(profile.Name); sync {
 			if err := c.sync(tracker); err != nil {
 				return fmt.Errorf("Error synchronising secret for profile %s: %s", profile.Name, err)
@@ -281,17 +256,6 @@ func (c *ConfigSync) syncSslProfileCredentialsToDisk(profiles map[string]qdr.Ssl
 	}
 	return nil
 }
-func (c *ConfigSync) trackSslProfiles(config *qdr.RouterConfig, path string) error {
-	for _, profile := range config.SslProfiles {
-		secretName := profile.Name
-
-		if strings.HasSuffix(profile.Name, "-profile") {
-			secretName = strings.TrimSuffix(profile.Name, "-profile")
-		}
-		c.track(secretName, paths.Join(path, profile.Name))
-	}
-	return nil
-}
 
 func (c *ConfigSync) recoverTracking() error {
 	configmap, err := c.config.Get(c.key(c.routerConfigMap))
@@ -310,56 +274,3 @@ func (c *ConfigSync) recoverTracking() error {
 	}
 	return nil
 }
-
-type SyncTarget struct {
-	name   string
-	path   string
-	secret *corev1.Secret
-}
-
-func (s *SyncTarget) sync(secret *corev1.Secret) error {
-	if s.secret != nil && reflect.DeepEqual(s.secret.Data, secret.Data) {
-		return nil
-	}
-	if err := writeSecretToPath(secret, s.path); err != nil {
-		return err
-	}
-	s.secret = secret
-	return nil
-}
-
-func writeSecretToPath(secret *corev1.Secret, path string) error {
-	if err := mkdir(path); err != nil {
-		return err
-	}
-	for key, value := range secret.Data {
-		if err := os.WriteFile(paths.Join(path, key), value, 0777); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func mkdir(path string) error {
-	_, err := os.Stat(path)
-	if os.IsNotExist(err) {
-		err = os.Mkdir(path, 0777)
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func excludedProfiles() []string {
-	return []string{"skupper-amqps", "skupper-service-client"}
-}
-
-func isExcludedProfile(name string) bool {
-	for _, v := range excludedProfiles() {
-		if name == v {
-			return true
-		}
-	}
-	return false
-}
diff --git a/internal/kube/adaptor/profile.go b/internal/kube/adaptor/profile.go
new file mode 100644
index 00000000..57a83d8a
--- /dev/null
+++ b/internal/kube/adaptor/profile.go
@@ -0,0 +1,83 @@
+package adaptor
+
+import (
+	"os"
+	paths "path"
+	"reflect"
+	"strings"
+
+	corev1 "k8s.io/api/core/v1"
+)
+
+type SslProfileSyncer struct {
+	profiles map[string]*SslProfile
+	path     string
+}
+
+func newSslProfileSyncer(path string) *SslProfileSyncer {
+	return &SslProfileSyncer{
+		profiles: map[string]*SslProfile{},
+		path:     path,
+	}
+}
+
+func (s SslProfileSyncer) get(profile string) (*SslProfile, bool) {
+	secret := profile
+	if strings.HasSuffix(profile, "-profile") {
+		secret = strings.TrimSuffix(profile, "-profile")
+	}
+	if current, ok := s.profiles[secret]; ok {
+		return current, false
+	}
+	target := &SslProfile{
+		name: secret,
+		path: paths.Join(s.path, profile),
+	}
+	s.profiles[secret] = target
+	return target, true
+}
+
+func (s SslProfileSyncer) bySecretName(secret string) (*SslProfile, bool) {
+	current, ok := s.profiles[secret]
+	return current, ok
+}
+
+type SslProfile struct {
+	name   string
+	path   string
+	secret *corev1.Secret
+}
+
+func (s *SslProfile) sync(secret *corev1.Secret) error {
+	if s.secret != nil && reflect.DeepEqual(s.secret.Data, secret.Data) {
+		return nil
+	}
+	if err := writeSecretToPath(secret, s.path); err != nil {
+		return err
+	}
+	s.secret = secret
+	return nil
+}
+
+func writeSecretToPath(secret *corev1.Secret, path string) error {
+	if err := mkdir(path); err != nil {
+		return err
+	}
+	for key, value := range secret.Data {
+		if err := os.WriteFile(paths.Join(path, key), value, 0777); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func mkdir(path string) error {
+	_, err := os.Stat(path)
+	if os.IsNotExist(err) {
+		err = os.Mkdir(path, 0777)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
diff --git a/internal/nonkube/client/fs/connector_handler.go b/internal/nonkube/client/fs/connector_handler.go
index bceaec6c..a1e0c50e 100644
--- a/internal/nonkube/client/fs/connector_handler.go
+++ b/internal/nonkube/client/fs/connector_handler.go
@@ -38,23 +38,33 @@ func (s *ConnectorHandler) Add(resource v2alpha1.Connector) error {
 	return nil
 }
 
-func (s *ConnectorHandler) Get(name string) (*v2alpha1.Connector, error) {
+func (s *ConnectorHandler) Get(name string, opt GetOptions) (*v2alpha1.Connector, error) {
 	var context v2alpha1.Connector
 	fileName := name + ".yaml"
 
-	// First read from runtime directory, where output is found after bootstrap
-	// has run.  If no runtime connectors try and display configured connectors
-	err, file := s.ReadFile(s.pathProvider.GetRuntimeNamespace(), fileName, common.Connectors)
-	if err != nil {
-		fmt.Println("Site not initialized yet")
-		err, file = s.ReadFile(s.pathProvider.GetNamespace(), fileName, common.Connectors)
+	if opt.RuntimeFirst == true {
+		// First read from runtime directory, where output is found after bootstrap
+		// has run.  If no runtime connectors try and display configured connectors
+		err, file := s.ReadFile(s.pathProvider.GetRuntimeNamespace(), fileName, common.Connectors)
+		if err != nil {
+			fmt.Println("Site not initialized yet")
+			err, file = s.ReadFile(s.pathProvider.GetNamespace(), fileName, common.Connectors)
+			if err != nil {
+				return nil, err
+			}
+			if err := s.DecodeYaml(file, &context); err != nil {
+				return nil, err
+			}
+		}
+	} else {
+		// read from input directory to get lastest config
+		err, file := s.ReadFile(s.pathProvider.GetNamespace(), fileName, common.Connectors)
 		if err != nil {
 			return nil, err
 		}
-	}
-
-	if err = s.DecodeYaml(file, &context); err != nil {
-		return nil, err
+		if err := s.DecodeYaml(file, &context); err != nil {
+			return nil, err
+		}
 	}
 
 	return &context, nil
@@ -101,20 +111,3 @@ func (s *ConnectorHandler) List() ([]*v2alpha1.Connector, error) {
 	}
 	return connectors, nil
 }
-
-func (s *ConnectorHandler) Update(name string) (*v2alpha1.Connector, error) {
-	var context v2alpha1.Connector
-	fileName := name + ".yaml"
-
-	// read from input directory to get lastest config
-	err, file := s.ReadFile(s.pathProvider.GetNamespace(), fileName, common.Connectors)
-	if err != nil {
-		return nil, err
-	}
-
-	if err = s.DecodeYaml(file, &context); err != nil {
-		return nil, err
-	}
-
-	return &context, nil
-}
diff --git a/internal/nonkube/client/fs/custom_resource_handler.go b/internal/nonkube/client/fs/custom_resource_handler.go
index 22d76d1b..9575d7fa 100644
--- a/internal/nonkube/client/fs/custom_resource_handler.go
+++ b/internal/nonkube/client/fs/custom_resource_handler.go
@@ -11,10 +11,13 @@ import (
 	"sigs.k8s.io/yaml"
 )
 
+type GetOptions struct {
+	RuntimeFirst bool
+}
+
 type CustomResourceHandler[T any] interface {
 	Add(T) error
-	Update(T) error
-	Get(name string) (T, error)
+	Get(name string, opts GetOptions) (T, error)
 	List() ([]T, error)
 	Delete(name string) error
 
diff --git a/internal/nonkube/client/fs/router_access_handler.go b/internal/nonkube/client/fs/router_access_handler.go
index 8877da45..7876cfbb 100644
--- a/internal/nonkube/client/fs/router_access_handler.go
+++ b/internal/nonkube/client/fs/router_access_handler.go
@@ -33,7 +33,8 @@ func (s *RouterAccessHandler) Add(resource v2alpha1.RouterAccess) error {
 
 	return nil
 }
-func (s *RouterAccessHandler) Update(name string) (*v2alpha1.RouterAccess, error) { return nil, nil }
-func (s *RouterAccessHandler) Get(name string) (*v2alpha1.RouterAccess, error)    { return nil, nil }
-func (s *RouterAccessHandler) Delete(name string) error                           { return nil }
-func (s *RouterAccessHandler) List() ([]*v2alpha1.RouterAccess, error)            { return nil, nil }
+func (s *RouterAccessHandler) Get(name string, opts GetOptions) (*v2alpha1.RouterAccess, error) {
+	return nil, nil
+}
+func (s *RouterAccessHandler) Delete(name string) error                { return nil }
+func (s *RouterAccessHandler) List() ([]*v2alpha1.RouterAccess, error) { return nil, nil }
diff --git a/internal/nonkube/client/fs/site_handler.go b/internal/nonkube/client/fs/site_handler.go
index b465081a..a7e3c207 100644
--- a/internal/nonkube/client/fs/site_handler.go
+++ b/internal/nonkube/client/fs/site_handler.go
@@ -34,7 +34,6 @@ func (s *SiteHandler) Add(resource v2alpha1.Site) error {
 	return nil
 }
 
-func (s *SiteHandler) Update(name string) (*v2alpha1.Site, error) { return nil, nil }
-func (s *SiteHandler) Get(name string) (*v2alpha1.Site, error)    { return nil, nil }
-func (s *SiteHandler) Delete(name string) error                   { return nil }
-func (s *SiteHandler) List() ([]*v2alpha1.Site, error)            { return nil, nil }
+func (s *SiteHandler) Get(name string, opts GetOptions) (*v2alpha1.Site, error) { return nil, nil }
+func (s *SiteHandler) Delete(name string) error                                 { return nil }
+func (s *SiteHandler) List() ([]*v2alpha1.Site, error)                          { return nil, nil }
diff --git a/pkg/kube/site/resources/skupper-router-deployment.yaml b/pkg/kube/site/resources/skupper-router-deployment.yaml
index 2dfbfe0d..81b93fdd 100644
--- a/pkg/kube/site/resources/skupper-router-deployment.yaml
+++ b/pkg/kube/site/resources/skupper-router-deployment.yaml
@@ -34,22 +34,8 @@ spec:
     spec:
       containers:
       - env:
-        - name: APPLICATION_NAME
-          value: skupper-router
-        - name: POD_NAMESPACE
-          valueFrom:
-            fieldRef:
-              apiVersion: v1
-              fieldPath: metadata.namespace
-        - name: POD_IP
-          valueFrom:
-            fieldRef:
-              apiVersion: v1
-              fieldPath: status.podIP
-        - name: QDROUTERD_AUTO_MESH_DISCOVERY
-          value: QUERY
         - name: QDROUTERD_CONF
-          value: /etc/skupper-router/config/skrouterd.json
+          value: /etc/skupper-router-certs/skrouterd.json
         - name: QDROUTERD_CONF_TYPE
           value: json
         - name: SKUPPER_SITE_ID
@@ -93,8 +79,6 @@ spec:
         volumeMounts:
         - mountPath: /etc/skupper-router-certs/skupper-amqps/
           name: skupper-local-server
-        - mountPath: /etc/skupper-router/config/
-          name: router-config
         - mountPath: /etc/skupper-router-certs
           name: skupper-router-certs
       - env:
@@ -107,7 +91,7 @@ spec:
           value: {{ .SiteId }}
         - name: SKUPPER_SITE_NAME
           value: {{ .SiteName }}
-        - name: SKUPPER_CONFIG
+        - name: SKUPPER_ROUTER_CONFIG
           value: {{ .Group }}
         - name: SKUPPER_ROUTER_DEPLOYMENT
           value: {{ .Group }}
@@ -127,15 +111,22 @@ spec:
         volumeMounts:
         - mountPath: /etc/skupper-router-certs
           name: skupper-router-certs
+      initContainers:
+      - env:
+        - name: SKUPPER_ROUTER_CONFIG
+          value: {{ .Group }}
+        image: {{ .ConfigSyncImage.Name }}
+        imagePullPolicy: {{ .ConfigSyncImage.PullPolicy }}
+        name: config-init
+        command: ["/app/config-sync", "-init"]
+        volumeMounts:
+        - mountPath: /etc/skupper-router-certs
+          name: skupper-router-certs
       serviceAccount: {{ .ServiceAccount }}
       volumes:
       - name: skupper-local-server
         secret:
           defaultMode: 420
           secretName: skupper-local-server
-      - configMap:
-          defaultMode: 420
-          name: {{ .Group }}
-        name: router-config
       - emptyDir: {}
         name: skupper-router-certs
diff --git a/pkg/kube/site/site.go b/pkg/kube/site/site.go
index d1a209ff..42de6bae 100644
--- a/pkg/kube/site/site.go
+++ b/pkg/kube/site/site.go
@@ -117,7 +117,20 @@ func (s *Site) reconcile(siteDef *skupperv2alpha1.Site) error {
 				Prefix:       "mc",
 				Distribution: "multicast",
 			})
-			rc.SetNormalListeners(SSL_PROFILE_PATH)
+			rc.AddHealthAndMetricsListener(9090)
+			rc.AddListener(qdr.Listener{
+				Name: "amqp",
+				Host: "localhost",
+				Port: 5672,
+			})
+			rc.AddSslProfile(qdr.ConfigureSslProfile("skupper-local-server", SSL_PROFILE_PATH, true))
+			rc.AddListener(qdr.Listener{
+				Name:             "amqps",
+				Port:             5671,
+				SslProfile:       "skupper-local-server",
+				SaslMechanisms:   "EXTERNAL",
+				AuthenticatePeer: true,
+			})
 			routerConfig = &rc
 		}
 		s.initialised = true
diff --git a/pkg/qdr/qdr.go b/pkg/qdr/qdr.go
index c3785d3b..1463e810 100644
--- a/pkg/qdr/qdr.go
+++ b/pkg/qdr/qdr.go
@@ -64,25 +64,9 @@ func InitialConfig(id string, siteId string, version string, edge bool, helloAge
 	return config
 }
 
-func InitialConfigSkupperRouter(id string, siteId string, version string, edge bool, helloAge int, options types.RouterOptions, path string) RouterConfig {
-	routerConfig := InitialConfig(id, siteId, version, edge, helloAge)
-	routerConfig.Metadata.DataConnectionCount = options.DataConnectionCount
-
-	if options.Logging != nil {
-		ConfigureRouterLogging(&routerConfig, options.Logging)
-	}
-	routerConfig.AddAddress(Address{
-		Prefix:       "mc",
-		Distribution: "multicast",
-	})
-	routerConfig.SetListenersForMode(options, path)
-	return routerConfig
-}
-
-func (r *RouterConfig) SetNormalListeners(path string) {
-	r.Listeners = map[string]Listener{}
+func (r *RouterConfig) AddHealthAndMetricsListener(port int32) {
 	r.AddListener(Listener{
-		Port:        9090,
+		Port:        port,
 		Role:        "normal",
 		Http:        true,
 		HttpRootDir: "disabled",
@@ -90,31 +74,6 @@ func (r *RouterConfig) SetNormalListeners(path string) {
 		Healthz:     true,
 		Metrics:     true,
 	})
-	r.AddListener(Listener{
-		Name: "amqp",
-		Host: "localhost",
-		Port: types.AmqpDefaultPort,
-	})
-	r.AddSslProfile(ConfigureSslProfile("skupper-amqps", path, true))
-	r.AddListener(Listener{
-		Name:             "amqps",
-		Port:             types.AmqpsDefaultPort,
-		SslProfile:       "skupper-amqps",
-		SaslMechanisms:   "EXTERNAL",
-		AuthenticatePeer: true,
-	})
-	r.AddSslProfile(ConfigureSslProfile(types.ServiceClientSecret, path, false))
-}
-
-func (r *RouterConfig) SetListenersForMode(options types.RouterOptions, path string) {
-	r.SetNormalListeners(path)
-	if r.Metadata.Mode != ModeEdge {
-		r.AddSslProfile(ConfigureSslProfile(types.InterRouterProfile, path, true))
-		listeners := []Listener{InteriorListener(options), EdgeListener(options)}
-		for _, listener := range listeners {
-			r.AddListener(listener)
-		}
-	}
 }
 
 func NewBridgeConfig() BridgeConfig {


Test patch:
diff --git a/cmd/network-console-collector/internal/collector/addresses_test.go b/cmd/network-console-collector/internal/collector/addresses_test.go
new file mode 100644
index 00000000..a575c244
--- /dev/null
+++ b/cmd/network-console-collector/internal/collector/addresses_test.go
@@ -0,0 +1,152 @@
+package collector
+
+import (
+	"context"
+	"io"
+	"log/slog"
+	"sort"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/skupperproject/skupper/pkg/vanflow"
+	"github.com/skupperproject/skupper/pkg/vanflow/store"
+	"gotest.tools/poll"
+)
+
+func TestAddressManager(t *testing.T) {
+	tlog := slog.New(slog.NewTextHandler(io.Discard, nil))
+
+	type testPhase struct {
+		Input             []vanflow.Record
+		ExpectedAddresses []vanflow.Record
+	}
+	testCases := []struct {
+		Name   string
+		Phases []testPhase
+	}{
+		{
+			Name: "simple",
+			Phases: []testPhase{
+				{
+					Input: []vanflow.Record{ // incomplete
+						vanflow.ConnectorRecord{
+							BaseRecord: vanflow.NewBase("c1"), Address: nil,
+						},
+					},
+					ExpectedAddresses: []vanflow.Record{}, //none
+				}, {
+					Input: []vanflow.Record{ // now complete
+						vanflow.ConnectorRecord{
+							BaseRecord: vanflow.NewBase("c1"),
+							Address:    ptrTo("bingo"), Protocol: ptrTo("tcp"),
+						},
+						vanflow.ConnectorRecord{ // multiple
+							BaseRecord: vanflow.NewBase("c2"),
+							Address:    ptrTo("bingo"), Protocol: ptrTo("tcp"),
+						},
+					},
+					ExpectedAddresses: []vanflow.Record{
+						AddressRecord{Name: "bingo", Protocol: "tcp"},
+					},
+				}, {
+					Input:             []vanflow.Record{}, // empty
+					ExpectedAddresses: []vanflow.Record{}, // empty
+				},
+			},
+		},
+		{
+			Name: "listeners",
+			Phases: []testPhase{
+				{
+					Input: []vanflow.Record{ // now complete
+						vanflow.ListenerRecord{
+							BaseRecord: vanflow.NewBase("l1"),
+							Address:    ptrTo("bingo"), Protocol: ptrTo("tcp"),
+						},
+						vanflow.ListenerRecord{
+							BaseRecord: vanflow.NewBase("l2"),
+							Address:    ptrTo("bingo"), Protocol: ptrTo("yodel"),
+						},
+						vanflow.ListenerRecord{
+							BaseRecord: vanflow.NewBase("l3"),
+							Address:    ptrTo("poker"), Protocol: ptrTo("tcp"),
+						},
+						vanflow.ListenerRecord{
+							BaseRecord: vanflow.NewBase("l4"),
+							Address:    ptrTo("chess"), Protocol: ptrTo("tcp"),
+						},
+					},
+					ExpectedAddresses: []vanflow.Record{
+						AddressRecord{Name: "bingo", Protocol: "tcp"},
+						AddressRecord{Name: "bingo", Protocol: "yodel"},
+						AddressRecord{Name: "poker", Protocol: "tcp"},
+						AddressRecord{Name: "chess", Protocol: "tcp"},
+					},
+				}, {
+					Input:             []vanflow.Record{}, // empty
+					ExpectedAddresses: []vanflow.Record{}, // empty
+				},
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.Name, func(t *testing.T) {
+			stor := store.NewSyncMapStore(store.SyncMapStoreConfig{
+				Indexers: RecordIndexers(),
+			})
+
+			testCtx, cancel := context.WithCancel(context.Background())
+			defer cancel()
+			manager := newAddressManager(tlog, stor)
+			go manager.run(testCtx)()
+
+			for _, phase := range tc.Phases {
+				stor.Replace(wrapRecords(phase.Input...))
+				for _, e := range stor.List() {
+					manager.handleChangeEvent(addEvent{Record: e.Record}, stor)
+				}
+				ordered := func(addresses []store.Entry) {
+					sort.Slice(addresses, func(i, j int) bool {
+						left := addresses[i].Record.(AddressRecord)
+						right := addresses[j].Record.(AddressRecord)
+						return left.Name+left.Protocol < right.Name+right.Protocol
+					})
+
+				}
+				expected := wrapRecords(phase.ExpectedAddresses...)
+				ordered(expected)
+				poll.WaitOn(t, func(t poll.LogT) poll.Result {
+					addresses := stor.Index(store.TypeIndex, store.Entry{Record: AddressRecord{}})
+					ordered(addresses)
+					diff := cmp.Diff(
+						addresses,
+						expected,
+						cmpopts.IgnoreFields(AddressRecord{}, "ID"),
+						cmpopts.IgnoreFields(AddressRecord{}, "Start"),
+						cmpopts.IgnoreFields(store.Entry{}, "Metadata"),
+					)
+					if diff == "" {
+						return poll.Success()
+					}
+					return poll.Continue("diff: %s", diff)
+				})
+
+				for _, r := range phase.Input {
+					stor.Delete(r.Identity())
+					manager.handleChangeEvent(deleteEvent{Record: r}, stor)
+				}
+			}
+		})
+	}
+
+}
+
+func wrapRecords(records ...vanflow.Record) []store.Entry {
+	entries := make([]store.Entry, len(records))
+	for i := range records {
+		entries[i].Record = records[i]
+	}
+	return entries
+}
diff --git a/cmd/network-console-collector/internal/server/address_test.go b/cmd/network-console-collector/internal/server/address_test.go
index a4ae775d..19fe2804 100644
--- a/cmd/network-console-collector/internal/server/address_test.go
+++ b/cmd/network-console-collector/internal/server/address_test.go
@@ -23,6 +23,7 @@ func TestAddresses(t *testing.T) {
 	begin := time.Now()
 	testcases := []struct {
 		Records              []store.Entry
+		Parameters           map[string][]string
 		ExpectOK             bool
 		ExpectCount          int
 		ExpectTimeRangeCount int
@@ -62,6 +63,42 @@ func TestAddresses(t *testing.T) {
 				t.Error("expected to find address record for addr-1")
 			},
 		},
+		{
+			Records: wrapRecords(
+				collector.AddressRecord{ID: "addr-1", Name: "pizza", Protocol: "tcp", Start: begin},
+				collector.AddressRecord{ID: "addr-2", Name: "icecream", Protocol: "tcp", Start: begin},
+				collector.AddressRecord{ID: "addr-3", Name: "waffles", Protocol: "tcp", Start: begin},
+				vanflow.ConnectorRecord{BaseRecord: vanflow.NewBase("c1"), Address: ptrTo("pizza"), Protocol: ptrTo("tcp")},
+				vanflow.ListenerRecord{BaseRecord: vanflow.NewBase("l1"), Address: ptrTo("pizza"), Protocol: ptrTo("tcp")},
+			),
+			Parameters: map[string][]string{
+				"sortBy": {"isBound.asc"},
+			},
+			ExpectOK:    true,
+			ExpectCount: 3,
+			ExpectResults: func(t *testing.T, results []api.AddressRecord) {
+				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")
+			},
+		},
+		{
+			Records: wrapRecords(
+				collector.AddressRecord{ID: "addr-1", Name: "pizza", Protocol: "tcp", Start: begin},
+				collector.AddressRecord{ID: "addr-2", Name: "icecream", Protocol: "tcp", Start: begin},
+				collector.AddressRecord{ID: "addr-3", Name: "waffles", Protocol: "tcp", Start: begin},
+				vanflow.ConnectorRecord{BaseRecord: vanflow.NewBase("c1"), Address: ptrTo("pizza"), Protocol: ptrTo("tcp")},
+				vanflow.ListenerRecord{BaseRecord: vanflow.NewBase("l1"), Address: ptrTo("pizza"), Protocol: ptrTo("tcp")},
+			),
+			Parameters: map[string][]string{
+				"sortBy": {"isBound.desc"},
+			},
+			ExpectOK:    true,
+			ExpectCount: 3,
+			ExpectResults: func(t *testing.T, results []api.AddressRecord) {
+				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")
+			},
+		},
 	}
 
 	for _, tc := range testcases {
@@ -71,7 +108,7 @@ func TestAddresses(t *testing.T) {
 			for _, r := range tc.Records {
 				graph.(reset).Reindex(r.Record)
 			}
-			resp, err := c.AddressesWithResponse(context.TODO())
+			resp, err := c.AddressesWithResponse(context.TODO(), WithParameters(tc.Parameters))
 			assert.Check(t, err)
 			if tc.ExpectOK {
 				assert.Equal(t, resp.StatusCode(), 200)
diff --git a/pkg/kube/site/site_test.go b/pkg/kube/site/site_test.go
index b60bbe59..80d2090a 100644
--- a/pkg/kube/site/site_test.go
+++ b/pkg/kube/site/site_test.go
@@ -1058,7 +1058,6 @@ func createRouterConfigMock(s *Site) error {
 		Prefix:       "mc",
 		Distribution: "multicast",
 	})
-	rc.SetNormalListeners(SSL_PROFILE_PATH)
 
 	err := s.createRouterConfig(&rc)
 	if err != nil {
diff --git a/pkg/qdr/qdr_test.go b/pkg/qdr/qdr_test.go
index 7b05be23..d233b7ba 100644
--- a/pkg/qdr/qdr_test.go
+++ b/pkg/qdr/qdr_test.go
@@ -47,35 +47,6 @@ func TestInitialConfig(t *testing.T) {
 	}
 }
 
-func TestInitialConfigSkupperRouter(t *testing.T) {
-	routerOptions := types.RouterOptions{
-		MaxFrameSize:     1518,
-		LoadBalancerIp:   "1.2.3.4",
-		DisableMutualTLS: true,
-		Logging: []types.RouterLogConfig{
-			{
-				Module: "PROTOCOL",
-				Level:  "trace",
-			},
-		},
-	}
-	path := "/tmp/skupper-router"
-	routerConfig := InitialConfigSkupperRouter("foo", "bar", "1.2.3", false, 10, routerOptions, path)
-
-	if routerConfig.Metadata.Id != "foo" {
-		t.Errorf("Invalid id, expected 'foo' got %q", routerConfig.Metadata.Id)
-	}
-	if len(routerConfig.LogConfig) != 1 {
-		t.Errorf("Expect one log entry")
-	}
-	if routerConfig.Addresses["mc"].Distribution != "multicast" {
-		t.Errorf("Expect address to configured as multicast")
-	}
-	if routerConfig.IsEdge() {
-		t.Errorf("Expected interior mode")
-	}
-}
-
 func TestAddRemoveListener(t *testing.T) {
 	config := InitialConfig("foo", "bar", "undefined", true, 3)
 	config.AddListener(Listener{
diff --git a/pkg/site/link_test.go b/pkg/site/link_test.go
index cebf750d..f2e2ed79 100644
--- a/pkg/site/link_test.go
+++ b/pkg/site/link_test.go
@@ -4,7 +4,6 @@ import (
 	"fmt"
 	"testing"
 
-	"github.com/skupperproject/skupper/api/types"
 	skupperv2alpha1 "github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1"
 	"github.com/skupperproject/skupper/pkg/qdr"
 	"gotest.tools/assert"
@@ -15,11 +14,8 @@ func TestLink_Apply(t *testing.T) {
 	id := "router-1"
 	siteId := "site-1"
 	version := "v2.0"
-	isEdge := true
 	notEdge := false
 	helloAge := 10
-	sslPath := ""
-	options := types.RouterOptions{}
 
 	type fields struct {
 		name        string
@@ -43,7 +39,7 @@ func TestLink_Apply(t *testing.T) {
 				definition:  nil,
 			},
 			args: args{
-				current: qdr.InitialConfigSkupperRouter(id, siteId, version, notEdge, helloAge, options, sslPath),
+				current: qdr.InitialConfig(id, siteId, version, notEdge, helloAge),
 			},
 			want: false,
 		},
@@ -61,7 +57,7 @@ func TestLink_Apply(t *testing.T) {
 				},
 			},
 			args: args{
-				current: qdr.InitialConfigSkupperRouter(id, siteId, version, notEdge, helloAge, options, sslPath),
+				current: qdr.InitialConfig(id, siteId, version, notEdge, helloAge),
 			},
 			want: false,
 		},
@@ -87,7 +83,7 @@ func TestLink_Apply(t *testing.T) {
 				},
 			},
 			args: args{
-				current: qdr.InitialConfigSkupperRouter(id, siteId, version, notEdge, helloAge, options, sslPath),
+				current: qdr.InitialConfig(id, siteId, version, notEdge, helloAge),
 			},
 			want: true,
 		},
@@ -113,7 +109,7 @@ func TestLink_Apply(t *testing.T) {
 				},
 			},
 			args: args{
-				current: qdr.InitialConfigSkupperRouter(id, siteId, version, isEdge, helloAge, options, sslPath),
+				current: qdr.InitialConfig(id, siteId, version, true, helloAge),
 			},
 			want: true,
 		},
@@ -136,8 +132,6 @@ func TestLinkMap_Apply(t *testing.T) {
 	//	isEdge := true
 	notEdge := false
 	helloAge := 10
-	sslPath := ""
-	options := types.RouterOptions{}
 
 	type fields struct {
 		name        string
@@ -301,7 +295,7 @@ func TestLinkMap_Apply(t *testing.T) {
 		},
 	}
 	for _, tt := range tests {
-		routerConfig := qdr.InitialConfigSkupperRouter(id, siteId, version, notEdge, helloAge, options, sslPath)
+		routerConfig := qdr.InitialConfig(id, siteId, version, notEdge, helloAge)
 		linkMap := make(LinkMap)
 		t.Run(tt.name, func(t *testing.T) {
 			for i, link := range tt.links {