Repository: scionproto/scion
Pull request #3656 (2020-02-05 17:42:59)

Delete Discovery Service
It was never used, so we can remove it for now. If needed,
we can recover it from the tree later.

Also delete package healthpool because it was never used.


Co-authored-by: Lukas Vogel <lukedirtwalker@gmail.com>
Co-authored-by: Sergiu Costea <sergiu.costea@gmail.com>

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/scionproto/scion/3656)
<!-- Reviewable:end -->



Patch:
diff --git a/BUILD.bazel b/BUILD.bazel
index f977db09d..e5307c220 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -33,7 +33,6 @@ pkg_tar(
         "//go/border/braccept:braccept",
         "//go/integration/cert_req:cert_req",
         "//go/integration/cert_req_integration:cert_req_integration",
-        "//go/examples/discovery_client:discovery_client",
         "//go/integration/end2end:end2end",
         "//go/integration/end2end_integration:end2end_integration",
         "//go/lib/xtest/graphupdater:graphupdater",
diff --git a/acceptance/discovery_util/Dockerfile b/acceptance/discovery_util/Dockerfile
deleted file mode 100644
index db2b904c5..000000000
--- a/acceptance/discovery_util/Dockerfile
+++ /dev/null
@@ -1,9 +0,0 @@
-FROM alpine
-
-ARG port
-# Must set env var to make PORT available in CMD
-ENV PORT ${port}
-
-RUN apk add --update mini_httpd && rm -rf /var/cache/apk/*
-
-CMD mini_httpd -C /etc/mini_httpd/mini_httpd.conf -D -p ${PORT}
diff --git a/acceptance/discovery_util/dc.tmpl b/acceptance/discovery_util/dc.tmpl
deleted file mode 100644
index 2ba154c8f..000000000
--- a/acceptance/discovery_util/dc.tmpl
+++ /dev/null
@@ -1,7 +0,0 @@
-  mock_ds1-ff00_0_111-1:
-    image: scion_discovery_test
-    volumes:
-    - ${DISC_DIR}:/var/www/localhost/htdocs:ro
-    networks:
-      REPLACE_NETWORK:
-        ipv4_address: ${DISC_IP}
diff --git a/acceptance/discovery_util/util.sh b/acceptance/discovery_util/util.sh
deleted file mode 100644
index 63a33a6e2..000000000
--- a/acceptance/discovery_util/util.sh
+++ /dev/null
@@ -1,155 +0,0 @@
-#!/bin/bash
-
-. acceptance/common.sh
-
-IA=${IA:-1-ff00:0:111}
-IA_FILE="$(ia_file $IA)"
-AS_FILE="$(as_file $IA)"
-TOPO="gen/ISD1/AS$AS_FILE/br$IA_FILE-1/topology.json"
-
-UTIL_PATH="acceptance/discovery_util"
-TEST_TOPOLOGY="topology/Tiny4.topo"
-
-HTTP_DIR="gen/discovery_acceptance"
-STATIC_DIR="$HTTP_DIR/discovery/v1/static"
-STATIC_FULL="$STATIC_DIR/full.json"
-STATIC_DEFAULT="$STATIC_DIR/default.json"
-DYNAMIC_DIR="$HTTP_DIR/discovery/v1/dynamic"
-DYNAMIC_FULL="$DYNAMIC_DIR/full.json"
-DYNAMIC_DEFAULT="$DYNAMIC_DIR/default.json"
-
-
-base_setup() {
-    set -e
-    # Create topology setup all necessary config files.
-    ./scion.sh topology -c "$TEST_TOPOLOGY" -d -t -ds
-    # Create the topology directories for serving.
-    mkdir -p "$STATIC_DIR"
-    mkdir -p "$DYNAMIC_DIR"
-
-    # Get ip and port for the discovery service.
-    local addr=$( jq -r '.DiscoveryService[].Addrs[].Public | "\(.Addr) \(.L4Port)"' "$TOPO" )
-
-    # Build mock discovery service container.
-    docker build -f "$UTIL_PATH/Dockerfile" -t "scion_discovery_test:latest" $UTIL_PATH --build-arg port=$( echo $addr | awk '{printf $2}' )
-
-    export DISC_IP=$( echo $addr | awk '{printf $1}' )
-    # Find absolute path of the scion dir in the docker compose file.
-    # This allows the test to work locally and on the CI.
-    export DISC_DIR="$( grep -oh '\/.*\/gen' gen/scion-dc.yml | grep -v ':' -m 1 )/discovery_acceptance"
-
-    # Get the network AS 1-ff00_0_111 is on. And replace it in the template.
-    local network=$(awk '/  scion_disp_cs1-ff00_0_111-1:/,/ volumes/ {if (f=="networks:") {gsub(":", "",$1); print $1}} {f=$1}' gen/scion-dc.yml)
-    # Modify docker compose file to contain mock discovery service.
-    sed -e "s/REPLACE_NETWORK/$network/" "$UTIL_PATH/dc.tmpl" | sed -i -e "/services:/r /dev/stdin" "gen/scion-dc.yml"
-}
-
-base_start_scion() {
-    ./scion.sh run nobuild
-    ./tools/dc scion up -d 'mock_ds1-ff00_0_111-1'
-}
-
-set_log_lvl() {
-    sed -i -e 's/Level = .*$/Level = "trace"/g' -e '/\[logging\.file\]/a FlushInterval = 1' "$1"
-}
-
-set_interval() {
-    sed -i -e "/\[discovery.$2]/a Interval = \"100ms\"" "$1"
-}
-
-set_connect() {
-    printf "\n[discovery.$2.Connect]\nInitialPeriod = \"$3\"" >> "$1"
-}
-
-set_fail_action() {
-    sed -i -e "/\[discovery.$2.Connect]/a FailAction = \"$3\"" "$1"
-}
-
-check_file() {
-    curl -f -s -S "$( jq -r '.DiscoveryService[].Addrs[].Public | "\(.Addr):\(.L4Port)"' "$TOPO" )/discovery/v1/$1/full.json" > /dev/null
-    curl -f -s -S "$( jq -r '.DiscoveryService[].Addrs[].Public | "\(.Addr):\(.L4Port)"' "$TOPO" )/discovery/v1/$1/default.json" > /dev/null
-}
-
-
-check_infra_fail_action() {
-    stop_mock_ds
-    # Check that services continue if fail action is not set.
-    for cfg in gen/ISD1/AS$AS_FILE/*/{cs,sd}.toml; do
-        set_connect "$cfg" "$1" "3s"
-    done
-    ./tools/dc scion restart "scion_cs$IA_FILE-1" "scion_sd$IA_FILE"
-    sleep 10
-    check_running "cs$IA_FILE-1" || fail "Error: cs$IA_FILE-1 not running"
-    check_running "sd$IA_FILE" || fail "Error: sd$IA_FILE not running"
-
-    # Check that services exit if fail action is fatal
-    for cfg in gen/ISD1/AS$AS_FILE/*/{cs,sd}.toml; do
-        set_fail_action "$cfg" "$1" "Fatal"
-    done
-    ./tools/dc scion restart "scion_cs$IA_FILE-1" "scion_sd$IA_FILE"
-    sleep 10
-    check_not_running "cs$IA_FILE-1" || fail "Error: cs$IA_FILE-1 still running"
-    check_not_running "sd$IA_FILE" || fail "Error: sd$IA_FILE still running"
-}
-
-check_br_fail_action() {
-    stop_mock_ds
-    # Check that border router continues if fail action is not set.
-    set_connect "gen/ISD1/AS$AS_FILE/br$IA_FILE-1/br.toml" "$1" "5s"
-    ./tools/dc scion restart "scion_br$IA_FILE-1"
-    sleep 10
-    check_running "br$IA_FILE-1" || fail "Error: br$IA_FILE-1 not running"
-
-    # Check that border router exits if fail action is fatal
-    set_fail_action "gen/ISD1/AS$AS_FILE/br$IA_FILE-1/br.toml" "$1" "Fatal"
-    ./tools/dc scion restart "scion_br$IA_FILE-1"
-    sleep 10
-    check_not_running "br$IA_FILE-1" || fail "Error: br$IA_FILE-1 still running"
-}
-
-stop_mock_ds() {
-    ./tools/dc scion stop 'mock_ds1-ff00_0_111-1'
-}
-
-check_running() {
-    if is_running_in_docker; then
-            local docker="docker_"
-    fi
-    docker top "scion_${docker}$1"
-}
-
-check_not_running() {
-    check_running $1 || local running="nope"
-    [ "$running" == "nope" ] || return 1
-}
-
-print_help() {
-    echo
-	cat <<-_EOF
-	    $PROGRAM name
-	        return the name of this test
-	    $PROGRAM setup
-	        execute only the setup phase.
-	    $PROGRAM run
-	        execute only the run phase.
-	    $PROGRAM teardown
-	        execute only the teardown phase.
-	_EOF
-}
-
-PROGRAM=`basename "$0"`
-COMMAND="$1"
-
-do_command() {
-    PROGRAM="$1"
-    COMMAND="$2"
-    TEST_NAME="$3"
-    shift 3
-    case "$COMMAND" in
-        name)
-            echo $TEST_NAME ;;
-        setup|run|teardown)
-            "test_$COMMAND" "$@" ;;
-        *) print_help; exit 1 ;;
-    esac
-}
diff --git a/go/border/BUILD.bazel b/go/border/BUILD.bazel
index 14170d32f..bdb81407c 100644
--- a/go/border/BUILD.bazel
+++ b/go/border/BUILD.bazel
@@ -24,10 +24,8 @@ go_library(
         "//go/border/rpkt:go_default_library",
         "//go/lib/assert:go_default_library",
         "//go/lib/common:go_default_library",
-        "//go/lib/discovery:go_default_library",
         "//go/lib/env:go_default_library",
         "//go/lib/fatal:go_default_library",
-        "//go/lib/infra/modules/idiscovery:go_default_library",
         "//go/lib/infra/modules/itopo:go_default_library",
         "//go/lib/layers:go_default_library",
         "//go/lib/log:go_default_library",
diff --git a/go/border/brconf/BUILD.bazel b/go/border/brconf/BUILD.bazel
index 758cf8bc6..febb585d4 100644
--- a/go/border/brconf/BUILD.bazel
+++ b/go/border/brconf/BUILD.bazel
@@ -15,7 +15,6 @@ go_library(
         "//go/lib/common:go_default_library",
         "//go/lib/config:go_default_library",
         "//go/lib/env:go_default_library",
-        "//go/lib/infra/modules/idiscovery:go_default_library",
         "//go/lib/keyconf:go_default_library",
         "//go/lib/topology:go_default_library",
     ],
@@ -28,7 +27,6 @@ go_test(
     embed = [":go_default_library"],
     deps = [
         "//go/lib/env/envtest:go_default_library",
-        "//go/lib/infra/modules/idiscovery/idiscoverytest:go_default_library",
         "@com_github_burntsushi_toml//:go_default_library",
         "@com_github_stretchr_testify//assert:go_default_library",
     ],
diff --git a/go/border/brconf/params.go b/go/border/brconf/params.go
index b1e669198..d74e2e1cd 100644
--- a/go/border/brconf/params.go
+++ b/go/border/brconf/params.go
@@ -20,19 +20,17 @@ import (
 	"github.com/scionproto/scion/go/lib/common"
 	"github.com/scionproto/scion/go/lib/config"
 	"github.com/scionproto/scion/go/lib/env"
-	"github.com/scionproto/scion/go/lib/infra/modules/idiscovery"
 )
 
 var _ config.Config = (*Config)(nil)
 
 // Config is the border router configuration that is loaded from file.
 type Config struct {
-	General   env.General
-	Features  env.Features
-	Logging   env.Logging
-	Metrics   env.Metrics
-	Discovery Discovery
-	BR        BR
+	General  env.General
+	Features env.Features
+	Logging  env.Logging
+	Metrics  env.Metrics
+	BR       BR
 }
 
 func (cfg *Config) InitDefaults() {
@@ -41,7 +39,6 @@ func (cfg *Config) InitDefaults() {
 		&cfg.Features,
 		&cfg.Logging,
 		&cfg.Metrics,
-		&cfg.Discovery,
 		&cfg.BR,
 	)
 }
@@ -52,7 +49,6 @@ func (cfg *Config) Validate() error {
 		&cfg.Features,
 		&cfg.Logging,
 		&cfg.Metrics,
-		&cfg.Discovery,
 		&cfg.BR,
 	)
 }
@@ -63,7 +59,6 @@ func (cfg *Config) Sample(dst io.Writer, path config.Path, _ config.CtxMap) {
 		&cfg.Features,
 		&cfg.Logging,
 		&cfg.Metrics,
-		&cfg.Discovery,
 		&cfg.BR,
 	)
 }
@@ -101,20 +96,6 @@ func (cfg *BR) ConfigName() string {
 	return "br"
 }
 
-var _ config.Config = (*Discovery)(nil)
-
-type Discovery struct {
-	idiscovery.Config
-	// AllowSemiMutable indicates whether changes to the semi-mutable
-	// section in the static topology are allowed.
-	AllowSemiMutable bool
-}
-
-func (cfg *Discovery) Sample(dst io.Writer, path config.Path, ctx config.CtxMap) {
-	config.WriteString(dst, discoverySample)
-	cfg.Config.Sample(dst, path, ctx)
-}
-
 type FailAction string
 
 const (
diff --git a/go/border/brconf/sample.go b/go/border/brconf/sample.go
index 8b771db80..bae3c6fb6 100644
--- a/go/border/brconf/sample.go
+++ b/go/border/brconf/sample.go
@@ -24,9 +24,3 @@ Profile = false
 # (Fatal | Continue) (default Fatal)
 RollbackFailAction = "Fatal"
 `
-
-const discoverySample = `
-# Allow changes to the semi-mutable section during updates to the static
-# topology fetched from the discovery service. (default false)
-AllowSemiMutable = false
-`
diff --git a/go/border/router.go b/go/border/router.go
index fb765e51f..7372dd8c8 100644
--- a/go/border/router.go
+++ b/go/border/router.go
@@ -28,7 +28,6 @@ import (
 	"github.com/scionproto/scion/go/border/rpkt"
 	"github.com/scionproto/scion/go/lib/assert"
 	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/fatal"
 	"github.com/scionproto/scion/go/lib/log"
 	"github.com/scionproto/scion/go/lib/ringbuf"
 	_ "github.com/scionproto/scion/go/lib/scrypto" // Make sure math/rand is seeded
@@ -48,9 +47,7 @@ type Router struct {
 	// pktErrorQ is a channel for handling packet errors
 	pktErrorQ chan pktErrorArgs
 	// setCtxMtx serializes modifications to the router context. Topology updates
-	// can either be caused by a sighup reload, receiving an updated dynamic or
-	// static topology from the discovery service, or from dropping an expired
-	// dynamic topology.
+	// can be caused by a SIGHUP reload.
 	setCtxMtx sync.Mutex
 }
 
@@ -73,9 +70,6 @@ func (r *Router) Start() {
 		defer log.LogPanicAndExit()
 		rctrl.Control(r.sRevInfoQ, cfg.General.ReconnectToDispatcher)
 	}()
-	if err := r.startDiscovery(); err != nil {
-		fatal.Fatal(common.NewBasicError("Unable to start discovery", err))
-	}
 }
 
 // ReloadConfig handles reloading the configuration when SIGHUP is received.
diff --git a/go/border/setup.go b/go/border/setup.go
index cdd84351f..e506aa563 100644
--- a/go/border/setup.go
+++ b/go/border/setup.go
@@ -20,20 +20,13 @@
 package main
 
 import (
-	"fmt"
-	"net"
-	"net/http"
-	"time"
-
 	"github.com/syndtr/gocapability/capability"
 
 	"github.com/scionproto/scion/go/border/brconf"
 	"github.com/scionproto/scion/go/border/rctx"
 	"github.com/scionproto/scion/go/border/rpkt"
 	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/discovery"
 	"github.com/scionproto/scion/go/lib/fatal"
-	"github.com/scionproto/scion/go/lib/infra/modules/idiscovery"
 	"github.com/scionproto/scion/go/lib/infra/modules/itopo"
 	"github.com/scionproto/scion/go/lib/log"
 	"github.com/scionproto/scion/go/lib/ringbuf"
@@ -76,8 +69,8 @@ func (r *Router) setup() error {
 		return err
 	}
 	// Initialize itopo.
-	itopo.Init(r.Id, proto.ServiceType_br, itopo.Callbacks{CleanDynamic: r.setupCtxOnClean})
-	if _, _, err := itopo.SetStatic(conf.Topo, true); err != nil {
+	itopo.Init(r.Id, proto.ServiceType_br, itopo.Callbacks{})
+	if _, _, err := itopo.SetStatic(conf.Topo); err != nil {
 		return err
 	}
 	// Setup new context.
@@ -127,13 +120,12 @@ func (r *Router) setupCtxFromConfig(config *brconf.BRConf) error {
 	// We want to keep in sync itopo and the context that is set.
 	// We attempt to set the context with the topology that will be current
 	// after setting itopo. If setting itopo fails in the end, we rollback the context.
-	tx, err := itopo.BeginSetStatic(config.Topo.Writable(), true)
+	tx, err := itopo.BeginSetStatic(config.Topo.Writable())
 	if err != nil {
 		return err
 	}
 	// Set config to use the appropriate topology. The returned topology is
-	// not necessarily the same as config.Topo. It can be another static
-	// or dynamic topology.
+	// not necessarily the same as config.Topo. It can be another static topology.
 	newConf, err := brconf.WithNewTopo(r.Id, topology.FromRWTopology(tx.Get()), config)
 	if err != nil {
 		return err
@@ -146,30 +138,19 @@ func (r *Router) setupCtxFromConfig(config *brconf.BRConf) error {
 func (r *Router) setupCtxFromStatic(topo *topology.RWTopology) (bool, error) {
 	r.setCtxMtx.Lock()
 	defer r.setCtxMtx.Unlock()
-	tx, err := itopo.BeginSetStatic(topo, cfg.Discovery.AllowSemiMutable)
-	return r.setupCtxFromTopoUpdate(discovery.Static, tx, err)
-}
-
-// setupCtxFromDynamic sets up a new router context after receiving an updated
-// dynamic topology from the discovey service.
-func (r *Router) setupCtxFromDynamic(topo *topology.RWTopology) (bool, error) {
-	r.setCtxMtx.Lock()
-	defer r.setCtxMtx.Unlock()
-	tx, err := itopo.BeginSetDynamic(topo)
-	return r.setupCtxFromTopoUpdate(discovery.Dynamic, tx, err)
+	tx, err := itopo.BeginSetStatic(topo)
+	return r.setupCtxFromTopoUpdate(tx, err)
 }
 
 // setupCtxFromTopoUpdate sets up a new router context given a itopo.Transaction.
-func (r *Router) setupCtxFromTopoUpdate(mode discovery.Mode, tx itopo.Transaction,
-	err error) (bool, error) {
-
+func (r *Router) setupCtxFromTopoUpdate(tx itopo.Transaction, err error) (bool, error) {
 	if err != nil {
 		return false, err
 	}
 	if !tx.IsUpdate() {
 		return false, nil
 	}
-	log.Trace("====> Setting up new context from topology update", "mode", mode)
+	log.Trace("====> Setting up new context from topology update")
 	newConf, err := brconf.WithNewTopo(r.Id, topology.FromRWTopology(tx.Get()), rctx.Get().Conf)
 	if err != nil {
 		return false, err
@@ -177,21 +158,6 @@ func (r *Router) setupCtxFromTopoUpdate(mode discovery.Mode, tx itopo.Transactio
 	return true, r.setupNewContext(rctx.New(newConf), &tx)
 }
 
-// setupCtxOnClean sets up a new router context after the dynamic topology has expired.
-func (r *Router) setupCtxOnClean() {
-	log.Trace("====> Setting up new context on dynamic topology cleanup")
-	r.setCtxMtx.Lock()
-	defer r.setCtxMtx.Unlock()
-	newConf, err := brconf.WithNewTopo(r.Id, itopo.Get(), rctx.Get().Conf)
-	if err != nil {
-		log.Error("Unable to create new conf on dynamic cleanup", "err", err)
-		return
-	}
-	if err := r.setupNewContext(rctx.New(newConf), nil); err != nil {
-		log.Error("Unable to set context on dynamic cleanup", "err", err)
-	}
-}
-
 // setupNewContext sets up a new router context.
 func (r *Router) setupNewContext(ctx *rctx.Ctx, tx *itopo.Transaction) error {
 	oldCtx := rctx.Get()
@@ -281,54 +247,6 @@ func (r *Router) teardownNet(ctx, oldCtx *rctx.Ctx, sockConf brconf.SockConf) {
 	}
 }
 
-// startDiscovery starts automatic topology fetching from the discovery service if enabled.
-func (r *Router) startDiscovery() error {
-	var err error
-	var client *http.Client
-	if cfg.Discovery.Dynamic.Enable {
-		if client, err = r.discoveryClient(); err != nil {
-			return common.NewBasicError("Unable to create discovery client", err)
-		}
-	}
-	handlers := idiscovery.TopoHandlers{
-		Static:  r.setupCtxFromStatic,
-		Dynamic: r.setupCtxFromDynamic,
-	}
-	_, err = idiscovery.StartRunners(cfg.Discovery.Config, discovery.Full,
-		handlers, client, "border")
-	if err != nil {
-		return common.NewBasicError("Unable to start discovery runners", err)
-	}
-	return nil
-}
-
-// discoveryClient returns a client with the source address set to the internal address.
-func (r *Router) discoveryClient() (*http.Client, error) {
-	internalAddr := rctx.Get().Conf.BR.InternalAddr
-	tcpAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:0", internalAddr.IP))
-	if err != nil {
-		return nil, err
-	}
-	// The border router needs to use the correct source address to make sure
-	// it is on the ACL. The local address is set to internal address of the border router.
-	client := &http.Client{
-		Transport: &http.Transport{
-			Proxy: http.ProxyFromEnvironment,
-			DialContext: (&net.Dialer{
-				LocalAddr: tcpAddr,
-				Timeout:   30 * time.Second,
-				KeepAlive: 30 * time.Second,
-				DualStack: true,
-			}).DialContext,
-			MaxIdleConns:          100,
-			IdleConnTimeout:       90 * time.Second,
-			TLSHandshakeTimeout:   10 * time.Second,
-			ExpectContinueTimeout: 1 * time.Second,
-		},
-	}
-	return client, nil
-}
-
 // startSocks starts all sockets for the given context.
 func startSocks(ctx *rctx.Ctx) {
 	// Start local input functions.
diff --git a/go/cs/BUILD.bazel b/go/cs/BUILD.bazel
index 7f494c92e..b37fc23ec 100644
--- a/go/cs/BUILD.bazel
+++ b/go/cs/BUILD.bazel
@@ -21,14 +21,12 @@ go_library(
         "//go/cs/segutil:go_default_library",
         "//go/lib/addr:go_default_library",
         "//go/lib/common:go_default_library",
-        "//go/lib/discovery:go_default_library",
         "//go/lib/env:go_default_library",
         "//go/lib/fatal:go_default_library",
         "//go/lib/infra:go_default_library",
         "//go/lib/infra/infraenv:go_default_library",
         "//go/lib/infra/messenger:go_default_library",
         "//go/lib/infra/messenger/tcp:go_default_library",
-        "//go/lib/infra/modules/idiscovery:go_default_library",
         "//go/lib/infra/modules/itopo:go_default_library",
         "//go/lib/infra/modules/segfetcher:go_default_library",
         "//go/lib/infra/modules/seghandler:go_default_library",
diff --git a/go/cs/config/BUILD.bazel b/go/cs/config/BUILD.bazel
index 5af365106..713b04226 100644
--- a/go/cs/config/BUILD.bazel
+++ b/go/cs/config/BUILD.bazel
@@ -16,7 +16,6 @@ go_library(
         "//go/lib/config:go_default_library",
         "//go/lib/ctrl/path_mgmt:go_default_library",
         "//go/lib/env:go_default_library",
-        "//go/lib/infra/modules/idiscovery:go_default_library",
         "//go/lib/pathstorage:go_default_library",
         "//go/lib/serrors:go_default_library",
         "//go/lib/truststorage:go_default_library",
@@ -32,7 +31,6 @@ go_test(
         "//go/cs/beaconstorage/beaconstoragetest:go_default_library",
         "//go/lib/ctrl/path_mgmt:go_default_library",
         "//go/lib/env/envtest:go_default_library",
-        "//go/lib/infra/modules/idiscovery/idiscoverytest:go_default_library",
         "//go/lib/pathstorage/pathstoragetest:go_default_library",
         "//go/lib/truststorage/truststoragetest:go_default_library",
         "//go/lib/util:go_default_library",
diff --git a/go/cs/config/config.go b/go/cs/config/config.go
index 7b9bbcc50..95b7b70ea 100644
--- a/go/cs/config/config.go
+++ b/go/cs/config/config.go
@@ -24,7 +24,6 @@ import (
 	"github.com/scionproto/scion/go/lib/config"
 	"github.com/scionproto/scion/go/lib/ctrl/path_mgmt"
 	"github.com/scionproto/scion/go/lib/env"
-	"github.com/scionproto/scion/go/lib/infra/modules/idiscovery"
 	"github.com/scionproto/scion/go/lib/pathstorage"
 	"github.com/scionproto/scion/go/lib/serrors"
 	"github.com/scionproto/scion/go/lib/truststorage"
@@ -90,7 +89,6 @@ type Config struct {
 	SCIOND         env.SCIONDClient `toml:"sd_client"`
 	TrustDB        truststorage.TrustDBConf
 	BeaconDB       beaconstorage.BeaconDBConf
-	Discovery      idiscovery.Config
 	BS             BSConfig
 	CS             CSConfig
 	PS             PSConfig
@@ -108,7 +106,6 @@ func (cfg *Config) InitDefaults() {
 		&cfg.SCIOND,
 		&cfg.TrustDB,
 		&cfg.BeaconDB,
-		&cfg.Discovery,
 		&cfg.BS,
 		&cfg.CS,
 		&cfg.PS,
@@ -125,7 +122,6 @@ func (cfg *Config) Validate() error {
 		&cfg.SCIOND,
 		&cfg.TrustDB,
 		&cfg.BeaconDB,
-		&cfg.Discovery,
 		&cfg.BS,
 		&cfg.CS,
 		&cfg.PS,
@@ -144,7 +140,6 @@ func (cfg *Config) Sample(dst io.Writer, path config.Path, _ config.CtxMap) {
 		&cfg.SCIOND,
 		&cfg.TrustDB,
 		&cfg.BeaconDB,
-		&cfg.Discovery,
 		&cfg.BS,
 		&cfg.CS,
 		&cfg.PS,
diff --git a/go/cs/main.go b/go/cs/main.go
index 1b150cb1f..24b1cbfad 100644
--- a/go/cs/main.go
+++ b/go/cs/main.go
@@ -48,14 +48,12 @@ import (
 	"github.com/scionproto/scion/go/cs/segutil"
 	"github.com/scionproto/scion/go/lib/addr"
 	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/discovery"
 	"github.com/scionproto/scion/go/lib/env"
 	"github.com/scionproto/scion/go/lib/fatal"
 	"github.com/scionproto/scion/go/lib/infra"
 	"github.com/scionproto/scion/go/lib/infra/infraenv"
 	"github.com/scionproto/scion/go/lib/infra/messenger"
 	"github.com/scionproto/scion/go/lib/infra/messenger/tcp"
-	"github.com/scionproto/scion/go/lib/infra/modules/idiscovery"
 	"github.com/scionproto/scion/go/lib/infra/modules/itopo"
 	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher"
 	"github.com/scionproto/scion/go/lib/infra/modules/seghandler"
@@ -358,13 +356,6 @@ func realMain() int {
 		log.Crit("Unable to initialize MAC generator", "err", err)
 		return 1
 	}
-	discoRunners, err := idiscovery.StartRunners(cfg.Discovery, discovery.Full,
-		idiscovery.TopoHandlers{}, nil, "cs")
-	if err != nil {
-		log.Crit("Unable to start topology fetcher", "err", err)
-		return 1
-	}
-	defer discoRunners.Kill()
 
 	if err := tasks.Start(); err != nil {
 		log.Crit("Unable to start tasks", "err", err)
@@ -742,7 +733,7 @@ func handleTopoUpdate() {
 }
 
 func initTopo(topo topology.Topology) error {
-	if _, _, err := itopo.SetStatic(topo, false); err != nil {
+	if _, _, err := itopo.SetStatic(topo); err != nil {
 		return serrors.WrapStr("Unable to set initial static topology", err)
 	}
 	infraenv.InitInfraEnvironment(cfg.General.Topology)
diff --git a/go/examples/discovery_client/BUILD.bazel b/go/examples/discovery_client/BUILD.bazel
deleted file mode 100644
index b4ada34b4..000000000
--- a/go/examples/discovery_client/BUILD.bazel
+++ /dev/null
@@ -1,24 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-load("//:scion.bzl", "scion_go_binary")
-
-go_library(
-    name = "go_default_library",
-    srcs = ["client.go"],
-    importpath = "github.com/scionproto/scion/go/examples/discovery_client",
-    visibility = ["//visibility:private"],
-    deps = [
-        "//go/lib/common:go_default_library",
-        "//go/lib/discovery:go_default_library",
-        "//go/lib/discovery/topofetcher:go_default_library",
-        "//go/lib/log:go_default_library",
-        "//go/lib/periodic:go_default_library",
-        "//go/lib/serrors:go_default_library",
-        "//go/lib/topology:go_default_library",
-    ],
-)
-
-scion_go_binary(
-    name = "discovery_client",
-    embed = [":go_default_library"],
-    visibility = ["//visibility:public"],
-)
diff --git a/go/examples/discovery_client/README.md b/go/examples/discovery_client/README.md
deleted file mode 100644
index 2e7870e32..000000000
--- a/go/examples/discovery_client/README.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Sample Discovery Service Client
-
-This is a sample client that uses the library support for querying
-the discovery service. To successfully request topology files,
-a discovery service needs to be running. `server.sh`starts a
-simple HTTP server that serves a static topology file.
-
-```bash
-./server.sh path/to/topology.json
-```
-
-The client application periodically fetches the topology file
-from the discovery service. The first topology file that is
-received is written to stdout. All subsequent topology
-files are simply logged.
-
-The client application has two ways of getting the initial topology
-file. Either it is specified through a flag, or the client is provided
-with an initial discovery service address to fetch it from.
-
-To provide the topology file, run the following command:
-
-```bash
-discovery_client -topo topology.json
-```
-
-The discovery service addresses are read from the topology file,
-and the client will periodically request topologies.
-
-To initially fetch the topology file from the discovery service,
-run the following command:
-
-```bash
-discovery_client -addr [127.0.0.1]:30084
-```
-
-To test automatic switching by the library, `./server.sh` can be
-supplied with a topology file that contains multiple discovery
-service entries. By default, the address and port of the first
-entry in the file are used. The script can be instructed to use
-a specific entry:
-
-```bash
-./server.sh path/to/topology.json 2
-```
-
-This will start an http server that listens on the address
-and port of the second entry in the topology file.
diff --git a/go/examples/discovery_client/client.go b/go/examples/discovery_client/client.go
deleted file mode 100644
index abec5ae4e..000000000
--- a/go/examples/discovery_client/client.go
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright 2018 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
-	"context"
-	"flag"
-	"fmt"
-	"io/ioutil"
-	"net"
-	"net/url"
-	"os"
-	"sync"
-	"time"
-
-	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/discovery"
-	"github.com/scionproto/scion/go/lib/discovery/topofetcher"
-	"github.com/scionproto/scion/go/lib/log"
-	"github.com/scionproto/scion/go/lib/periodic"
-	"github.com/scionproto/scion/go/lib/serrors"
-	"github.com/scionproto/scion/go/lib/topology"
-)
-
-var (
-	https    = flag.Bool("https", false, "Use https to connect")
-	full     = flag.Bool("full", false, "Request full topology")
-	dynamic  = flag.Bool("dynamic", false, "Request dynamic topology")
-	topoPath = flag.String("topo", "", "Topology file")
-	out      = flag.String("out", "", "Write topology to this file")
-	period   = flag.Duration("period", 2*time.Second, "Time between requests")
-	timeout  = flag.Duration("timeout", 2*time.Second, "Timeout for single request")
-	ds       = flag.String("addr", "", "Discovery service to query for initial topology "+
-		"(form \"host:port\" or \"[host]:port\"")
-)
-
-func main() {
-	os.Exit(realMain())
-}
-
-func realMain() int {
-	os.Setenv("TZ", "UTC")
-	log.AddLogConsFlags()
-	flag.Parse()
-	if err := log.SetupFromFlags(""); err != nil {
-		fmt.Fprintf(os.Stderr, "err: Unable to setup logging err=%s\n", err)
-		flag.Usage()
-		return 1
-	}
-	defer log.LogPanicAndExit()
-	if err := validateFlags(); err != nil {
-		log.Crit("Unable to validate flags", "err", err)
-		return 1
-	}
-	topo, err := getTopo()
-	if err != nil {
-		log.Crit("Unable to load topology", "err", err)
-		return 1
-	}
-	var writeOnce sync.Once
-	fetcher, err := topofetcher.New(
-		topo.DS,
-		discovery.FetchParams{
-			File:  file(),
-			Mode:  mode(),
-			Https: *https,
-		},
-		topofetcher.Callbacks{
-			Error: func(ctx context.Context, err error) {
-				log.FromCtx(ctx).Error("Unable to fetch topology", "err", err)
-			},
-			Update: func(ctx context.Context, topo *topology.RWTopology) {
-				log.FromCtx(ctx).Info("Fetched new topology",
-					"ia", topo.IA, "ts", topo.Timestamp)
-			},
-			Raw: func(ctx context.Context, raw common.RawBytes, _ *topology.RWTopology) {
-				writeOnce.Do(func() {
-					fmt.Println(string(raw))
-					if *out == "" {
-						return
-					}
-					if err := ioutil.WriteFile(*out, raw, 0666); err != nil {
-						log.FromCtx(ctx).Error("Unable to write topology file", "err", err)
-					}
-					log.FromCtx(ctx).Info("Topology file written", "file", out)
-				})
-			},
-		}, nil, "myProcess")
-	if err != nil {
-		log.Crit("Unable to initialize fetcher", "err", err)
-		return 1
-	}
-	log.Info("Starting periodic fetching", "period", *period)
-	runner := periodic.Start(fetcher, *period, *timeout)
-	defer runner.Stop()
-	select {}
-}
-
-func validateFlags() error {
-	if *topoPath == "" && *ds == "" {
-		return serrors.New("Topology and discovery service address not specified")
-	}
-	if *ds != "" && *topoPath != "" {
-		return serrors.New("Both topology and discovery service address specified")
-	}
-	if _, err := url.Parse(*ds); err != nil {
-		return err
-	}
-	return nil
-}
-
-func getTopo() (*topology.RWTopology, error) {
-	if *ds != "" {
-		log.Info("Fetch initial topology from discovery service", "addr", ds)
-		ctx, cancelF := context.WithTimeout(context.Background(), *timeout)
-		defer cancelF()
-
-		a, err := net.ResolveUDPAddr("udp", *ds)
-		if err != nil {
-			return nil, err
-		}
-
-		ret, _, err := discovery.FetchTopoRaw(ctx, discovery.FetchParams{
-			File:  file(),
-			Mode:  mode(),
-			Https: *https,
-		}, a,
-			nil)
-		return ret, err
-	}
-	log.Info("Load initial topology from disk", "topo", topoPath)
-	return topology.RWTopologyFromJSONFile(*topoPath)
-}
-
-func mode() discovery.Mode {
-	if *dynamic {
-		return discovery.Dynamic
-	}
-	return discovery.Static
-}
-
-func file() discovery.File {
-	if *full {
-		return discovery.Full
-	}
-	return discovery.Endhost
-}
diff --git a/go/examples/discovery_client/server.sh b/go/examples/discovery_client/server.sh
deleted file mode 100755
index e580aca10..000000000
--- a/go/examples/discovery_client/server.sh
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash
-
-BASE="discovery/v1"
-STATIC="static"
-DYNAMIC="dynamic"
-FULL="full.json"
-ENDHOST="endhost.json"
-
-ds_entry=$2
-if [ -z "$2" ]; then
-    ds_entry=1
-fi
-
-# Setup file structure.
-temp_dir=$( mktemp -d )
-printf "Created temp dir: $temp_dir\n"
-mkdir -p "$temp_dir/$BASE/$STATIC"
-mkdir -p "$temp_dir/$BASE/$DYNAMIC"
-cat $1 | tee $temp_dir/$BASE/{$STATIC,$DYNAMIC}/{$FULL,$ENDHOST} > /dev/null
-
-count=$( jq -r '.DiscoveryService | length' $1 )
-printf "Using entry $ds_entry out of $count\n"
-
-laddr=$( jq -r '.DiscoveryService[].Addrs.IPv4.Public.Addr' $1 | sed -n "${ds_entry}p" )
-port=$( jq -r '.DiscoveryService[].Addrs.IPv4.Public.L4Port' $1 | sed -n "${ds_entry}p" )
-
-# Start http server that serves the files in the temp directory.
-cd $temp_dir
-python3 -m http.server $port --bind $laddr 
diff --git a/go/lib/common/defs.go b/go/lib/common/defs.go
index 10aa653a3..41729d157 100644
--- a/go/lib/common/defs.go
+++ b/go/lib/common/defs.go
@@ -32,13 +32,10 @@ const (
 
 const (
 	BR  = "BR"
-	BS  = "BS"
-	PS  = "PS"
 	CS  = "CS"
 	SB  = "SB"
 	RS  = "RS"
 	SIG = "SIG"
-	DS  = "DS"
 
 	CPService = "Control Plane Service"
 )
diff --git a/go/lib/discovery/BUILD.bazel b/go/lib/discovery/BUILD.bazel
deleted file mode 100644
index 8e782ec5d..000000000
--- a/go/lib/discovery/BUILD.bazel
+++ /dev/null
@@ -1,18 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-
-go_library(
-    name = "go_default_library",
-    srcs = [
-        "common.go",
-        "discovery.go",
-    ],
-    importpath = "github.com/scionproto/scion/go/lib/discovery",
-    visibility = ["//visibility:public"],
-    deps = [
-        "//go/lib/common:go_default_library",
-        "//go/lib/periodic:go_default_library",
-        "//go/lib/serrors:go_default_library",
-        "//go/lib/topology:go_default_library",
-        "@org_golang_x_net//context/ctxhttp:go_default_library",
-    ],
-)
diff --git a/go/lib/discovery/common.go b/go/lib/discovery/common.go
deleted file mode 100644
index f981e9310..000000000
--- a/go/lib/discovery/common.go
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2018 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package discovery
-
-import (
-	"fmt"
-	"net"
-
-	"github.com/scionproto/scion/go/lib/periodic"
-	"github.com/scionproto/scion/go/lib/topology"
-)
-
-// InstancePool keeps a pool of known discovery service instances.
-type InstancePool interface {
-	// Update updates the pool based on a new discovery service map.
-	Update(topology.IDAddrMap) error
-	// Choose returns the info for the best discovery service instance
-	// according to the pool.
-	Choose() (InstanceInfo, error)
-}
-
-// InstanceInfo provides the information for a single discovery service instance.
-type InstanceInfo interface {
-	fmt.Stringer
-	// Update updates the address.
-	Update(*net.UDPAddr)
-	// Key returns the key of the instance.
-	Key() string
-	// Addr returns the address of the instance.
-	Addr() *net.UDPAddr
-	// FailCount returns a number indicating how often
-	// the instance has failed.
-	FailCount() int
-	// Fail adds to the fail count. This should be called by
-	// the client when it fails to reach the instance.
-	Fail()
-}
-
-// Fetcher is a periodic task that fetches topology form the discovery service.
-type Fetcher interface {
-	periodic.Task
-	// UpdateInstances updates the discovery service instances for the fetcher.
-	// It can be used to notify fetcher in case a new topology file has been
-	// received from sources other than the discovery service
-	// (e.g. through sighup reloading)
-	UpdateInstances(topology.IDAddrMap) error
-}
diff --git a/go/lib/discovery/discovery.go b/go/lib/discovery/discovery.go
deleted file mode 100644
index d1df10f82..000000000
--- a/go/lib/discovery/discovery.go
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright 2018 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package discovery provides library support to query the discovery
-// service for topology files.
-//
-// The discovery service serves topology files for its own AS. Clients can
-// use it to fetch an initial topology file, or to update their topology.
-// The topology files are served in two different modes and two different
-// privilege versions. Non privileged entities should only get the
-// necessary information of the topology.
-//
-// Modes
-//
-// Static: The topology file in static mode is updated infrequently. It
-// contains a subset of all available service instances which have stable
-// addresses and availability.
-//
-// Dynamic: The topology file in dynamic mode is updated frequently. Service
-// instances are dynamically added and removed based on their status. This
-// allows ASes to dynamically start and stop instances.
-//
-// Files
-//
-// There are two privilege versions of the topology file. The endhost version
-// is intended for end hosts and non-privileged entities. The full version is
-// only intended for privileged entities that need all topology information.
-//
-// Endhost: The endhost version of the topology file contains all the
-// information necessary for end hosts. Unnecessary information is stripped
-// from the file (e.g. border router interface addresses or beacon service
-// addresses).
-//
-// Full: The full version of the topology file contains all the information.
-// This file is only accessible by privileged entities (e.g infrastructure
-// elements).
-//
-// Default: When the default version of the topology file is requested, the
-// discovery service decides which version to serve based on the privilege
-// of the requester.
-//
-// Paths
-//
-// The topology files are fetched with a simple http get request. The path
-// is dependent on the mode and file version:
-//  static  && default:  /discovery/v1/static/default.json
-//  static  && endhost:  /discovery/v1/static/endhost.json
-//  static  && full:     /discovery/v1/static/full.json
-//  dynamic && default:  /discovery/v1/dynamic/default.json
-//  dynamic && endhost:  /discovery/v1/dynamic/endhost.json
-//  dynamic && full:     /discovery/v1/dynamic/full.json
-package discovery
-
-import (
-	"context"
-	"fmt"
-	"io/ioutil"
-	"net"
-	"net/http"
-
-	"golang.org/x/net/context/ctxhttp"
-
-	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/serrors"
-	"github.com/scionproto/scion/go/lib/topology"
-)
-
-// FetchParams contains the parameters for fetching the topology from
-// the discovery service.
-type FetchParams struct {
-	// Mode indicates whether the static or the dynamic topology is requested.
-	Mode Mode
-	// File indicates whether the full, endhost or default topology is requested.
-	File File
-	// Https indicates whether https should be used.
-	Https bool
-}
-
-type Mode string
-
-const (
-	// Dynamic indicates the dynamic mode.
-	Dynamic Mode = "dynamic"
-	// Static indicates the static mode.
-	Static Mode = "static"
-)
-
-type File string
-
-const (
-	// Full is the full topology file, including all service information.
-	Full File = "full.json"
-	// Endhost is a stripped down topology file for non-privileged entities.
-	Endhost File = "endhost.json"
-	// Default is a topology file whose content is based on the privilege of
-	// the requester.
-	Default File = "default.json"
-)
-
-const (
-	// Base is the base path for the topology file url. It is supposed to be used
-	// as Base/<mode>/<file>. For example, the dynamic and full topology has the url
-	// "discovery/v1/dynamic/full.json"
-	Base = "discovery/v1"
-)
-
-// FetchTopoRaw fetches the topology with the specified parameters from the
-// discovery service. If client is nil, the default http client is used.
-// Both the topology and the raw response body are returned.
-func FetchTopoRaw(ctx context.Context, params FetchParams, ds *net.UDPAddr,
-	client *http.Client) (*topology.RWTopology, common.RawBytes, error) {
-
-	url, err := createURL(params, ds)
-	if err != nil {
-		return nil, nil, common.NewBasicError("Unable to create URL", err)
-	}
-	rep, err := ctxhttp.Get(ctx, client, url)
-	if err != nil {
-		return nil, nil, common.NewBasicError("HTTP request failed", err)
-	}
-	defer rep.Body.Close()
-	if rep.StatusCode != http.StatusOK {
-		return nil, nil, common.NewBasicError("Status not OK", nil, "status", rep.Status)
-	}
-	raw, err := ioutil.ReadAll(rep.Body)
-	if err != nil {
-		return nil, nil, common.NewBasicError("Unable to read body", err)
-	}
-	topo, err := topology.RWTopologyFromJSONBytes(raw)
-	if err != nil {
-		return nil, nil, common.NewBasicError("Unable to parse topo", err)
-	}
-	return topo, raw, nil
-}
-
-// createURL builds the url to the topology file.
-func createURL(params FetchParams, ds *net.UDPAddr) (string, error) {
-	if ds == nil {
-		return "", serrors.New("Addr not set")
-	}
-	protocol := "http"
-	if params.Https {
-		protocol = "https"
-	}
-	return fmt.Sprintf("%s://%s:%d/%s", protocol, ds.IP, ds.Port,
-		Path(params.Mode, params.File)), nil
-}
-
-// Path creates the route to the topology file based on the mode and file.
-func Path(mode Mode, file File) string {
-	return fmt.Sprintf("%s/%s/%s", Base, mode, file)
-}
diff --git a/go/lib/discovery/discoveryinfo/BUILD.bazel b/go/lib/discovery/discoveryinfo/BUILD.bazel
deleted file mode 100644
index 111ae55bb..000000000
--- a/go/lib/discovery/discoveryinfo/BUILD.bazel
+++ /dev/null
@@ -1,16 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
-
-go_library(
-    name = "go_default_library",
-    srcs = ["info.go"],
-    importpath = "github.com/scionproto/scion/go/lib/discovery/discoveryinfo",
-    visibility = ["//visibility:public"],
-    deps = ["//go/lib/discovery:go_default_library"],
-)
-
-go_test(
-    name = "go_default_test",
-    srcs = ["info_test.go"],
-    embed = [":go_default_library"],
-    deps = ["@com_github_stretchr_testify//assert:go_default_library"],
-)
diff --git a/go/lib/discovery/discoveryinfo/info.go b/go/lib/discovery/discoveryinfo/info.go
deleted file mode 100644
index c2f510552..000000000
--- a/go/lib/discovery/discoveryinfo/info.go
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2018 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package discoveryinfo
-
-import (
-	"fmt"
-	"math"
-	"net"
-	"sync"
-	"time"
-
-	"github.com/scionproto/scion/go/lib/discovery"
-)
-
-const (
-	// failExpStart is the time after which fails are expired.
-	failExpStart = 5 * time.Minute
-	// failExpInterval is the interval between fail expiration.
-	failExpInterval = 10 * time.Second
-)
-
-var _ discovery.InstanceInfo = (*Info)(nil)
-
-// Info keeps track of the discovery service and its health.
-type Info struct {
-	mu        sync.Mutex
-	addr      *net.UDPAddr
-	key       string
-	lastFail  time.Time
-	lastExp   time.Time
-	failCount uint16
-}
-
-// New creates a new info with the specified key and address.
-func New(key string, addr *net.UDPAddr) *Info {
-	return &Info{
-		addr:     addr,
-		key:      key,
-		lastFail: time.Now(),
-		lastExp:  time.Now(),
-	}
-}
-
-// Update updates the address. If changed, the fail count is reset.
-func (h *Info) Update(a *net.UDPAddr) {
-	h.mu.Lock()
-	defer h.mu.Unlock()
-	if !h.addr.IP.Equal(a.IP) || h.addr.Port != a.Port {
-		h.addr.IP = append(h.addr.IP[:0:0], a.IP...)
-		h.addr.Port = a.Port
-		h.failCount = 0
-		h.lastFail = time.Now()
-		h.lastExp = time.Now()
-	}
-}
-
-// Key returns the info key.
-func (h *Info) Key() string {
-	return h.key
-}
-
-// Addr returns the address of the discovery service instance.
-func (h *Info) Addr() *net.UDPAddr {
-	h.mu.Lock()
-	defer h.mu.Unlock()
-	ret := &net.UDPAddr{}
-	ret.IP = append(h.addr.IP[:0:0], h.addr.IP...)
-	ret.Port = h.addr.Port
-	return ret
-}
-
-// FailCount returns the fail count.
-func (h *Info) FailCount() int {
-	h.mu.Lock()
-	defer h.mu.Unlock()
-	h.expireFails(time.Now())
-	return int(h.failCount)
-}
-
-// Fail adds to the fail count.
-func (h *Info) Fail() {
-	h.mu.Lock()
-	defer h.mu.Unlock()
-	h.expireFails(time.Now())
-	h.lastFail = time.Now()
-	if h.failCount < math.MaxInt16 {
-		h.failCount++
-	}
-}
-
-// expireFails reduces the fail count if the last fail is sufficiently long in the past.
-// The caller is assumed to hold the lock on h.
-func (h *Info) expireFails(now time.Time) {
-	if now.Sub(h.lastFail) > failExpStart && now.Sub(h.lastExp) > failExpInterval {
-		for i := 0; i < int(now.Sub(h.lastExp)/failExpInterval); i++ {
-			h.failCount /= 2
-		}
-		h.lastExp = now
-	}
-}
-
-func (h *Info) String() string {
-	h.mu.Lock()
-	defer h.mu.Unlock()
-	return fmt.Sprintf("Key: %s lastFail: %s failCount: %d", h.key, h.lastFail, h.failCount)
-}
diff --git a/go/lib/discovery/discoverypool/BUILD.bazel b/go/lib/discovery/discoverypool/BUILD.bazel
deleted file mode 100644
index 7098a0986..000000000
--- a/go/lib/discovery/discoverypool/BUILD.bazel
+++ /dev/null
@@ -1,27 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
-
-go_library(
-    name = "go_default_library",
-    srcs = ["pool.go"],
-    importpath = "github.com/scionproto/scion/go/lib/discovery/discoverypool",
-    visibility = ["//visibility:public"],
-    deps = [
-        "//go/lib/common:go_default_library",
-        "//go/lib/discovery:go_default_library",
-        "//go/lib/discovery/discoveryinfo:go_default_library",
-        "//go/lib/serrors:go_default_library",
-        "//go/lib/topology:go_default_library",
-    ],
-)
-
-go_test(
-    name = "go_default_test",
-    srcs = ["pool_test.go"],
-    data = glob(["testdata/**"]),
-    embed = [":go_default_library"],
-    deps = [
-        "//go/lib/topology:go_default_library",
-        "//go/lib/xtest:go_default_library",
-        "@com_github_smartystreets_goconvey//convey:go_default_library",
-    ],
-)
diff --git a/go/lib/discovery/discoverypool/pool.go b/go/lib/discovery/discoverypool/pool.go
deleted file mode 100644
index 75223c9cf..000000000
--- a/go/lib/discovery/discoverypool/pool.go
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2018 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package discoverypool
-
-import (
-	"math"
-	"sync"
-
-	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/discovery"
-	"github.com/scionproto/scion/go/lib/discovery/discoveryinfo"
-	"github.com/scionproto/scion/go/lib/serrors"
-	"github.com/scionproto/scion/go/lib/topology"
-)
-
-var _ discovery.InstancePool = (*Pool)(nil)
-
-// Pool maps discovery service instances to their info.
-type Pool struct {
-	mu sync.Mutex
-	m  map[string]discovery.InstanceInfo
-}
-
-// New populates the pool with discovery service instances from the map
-// in svcInfo. At least one instance must be present.
-func New(svcInfo topology.IDAddrMap) (*Pool, error) {
-	if len(svcInfo) <= 0 {
-		return nil, common.NewBasicError(
-			"SvcInfo must contain at least one discovery service instance", nil)
-	}
-	p := &Pool{
-		m: make(map[string]discovery.InstanceInfo, len(svcInfo)),
-	}
-	p.Update(svcInfo)
-	return p, nil
-}
-
-// Update adds missing instances and removes instances which are no longer in the topology.
-func (p *Pool) Update(svcInfo topology.IDAddrMap) error {
-	p.mu.Lock()
-	defer p.mu.Unlock()
-	// Add missing DS servers.
-	for k, v := range svcInfo {
-		if info, ok := p.m[k]; !ok {
-			p.m[k] = discoveryinfo.New(k, v.SCIONAddress)
-		} else {
-			info.Update(v.SCIONAddress)
-		}
-	}
-	// Get list of outdated DS servers.
-	var del []string
-	for k := range p.m {
-		if _, ok := svcInfo[k]; !ok {
-			del = append(del, k)
-		}
-	}
-	// Check that more than one DS remain.
-	if len(del) == len(p.m) {
-		return serrors.New("Unable to delete all discovery service instances")
-	}
-	for _, k := range del {
-		delete(p.m, k)
-	}
-	return nil
-}
-
-// Choose returns the info for the discovery service instance with the
-// minimal fail count in the pool.
-func (p *Pool) Choose() (discovery.InstanceInfo, error) {
-	p.mu.Lock()
-	defer p.mu.Unlock()
-	var best discovery.InstanceInfo
-	var minFail = math.MaxUint16
-	for _, ds := range p.m {
-		failCount := ds.FailCount()
-		if failCount < minFail {
-			best = ds
-			minFail = failCount
-		}
-	}
-	if best == nil {
-		return nil, serrors.New("Unable to find any discovery service instance")
-	}
-	return best, nil
-}
diff --git a/go/lib/discovery/topofetcher/BUILD.bazel b/go/lib/discovery/topofetcher/BUILD.bazel
deleted file mode 100644
index 742324c1b..000000000
--- a/go/lib/discovery/topofetcher/BUILD.bazel
+++ /dev/null
@@ -1,15 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-
-go_library(
-    name = "go_default_library",
-    srcs = ["fetcher.go"],
-    importpath = "github.com/scionproto/scion/go/lib/discovery/topofetcher",
-    visibility = ["//visibility:public"],
-    deps = [
-        "//go/lib/common:go_default_library",
-        "//go/lib/discovery:go_default_library",
-        "//go/lib/discovery/discoverypool:go_default_library",
-        "//go/lib/serrors:go_default_library",
-        "//go/lib/topology:go_default_library",
-    ],
-)
diff --git a/go/lib/discovery/topofetcher/fetcher.go b/go/lib/discovery/topofetcher/fetcher.go
deleted file mode 100644
index 73accd1e0..000000000
--- a/go/lib/discovery/topofetcher/fetcher.go
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright 2018 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package topofetcher
-
-import (
-	"context"
-	"fmt"
-	"net/http"
-
-	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/discovery"
-	"github.com/scionproto/scion/go/lib/discovery/discoverypool"
-	"github.com/scionproto/scion/go/lib/serrors"
-	"github.com/scionproto/scion/go/lib/topology"
-)
-
-var _ discovery.Fetcher = (*Fetcher)(nil)
-
-// Callbacks are used to inform the client. The functions are called when
-// an associated event occurs. If the function is nil, it is ignored.
-type Callbacks struct {
-	// Raw is called with the raw body from the discovery service response and the parsed topology.
-	Raw func(context.Context, common.RawBytes, *topology.RWTopology)
-	// Update is called with the parsed topology from the discovery service response.
-	Update func(context.Context, *topology.RWTopology)
-	// Error is called with any error that occurs.
-	Error func(context.Context, error)
-}
-
-// Fetcher is used to fetch a new topology file from the discovery service.
-type Fetcher struct {
-	// Pool is a Pool of discovery services
-	Pool discovery.InstancePool
-	// Params contains the parameters for fetching the topology.
-	Params discovery.FetchParams
-	// Callbacks contains the callbacks.
-	Callbacks Callbacks
-	// Client is the http Client. If nil, the default Client is used.
-	Client *http.Client
-	caller string
-}
-
-// New initializes a fetcher with the given values. Topo is provided to
-// initialize the pool with discovery services.
-func New(svcInfo topology.IDAddrMap, params discovery.FetchParams,
-	clbks Callbacks, client *http.Client, component string) (*Fetcher, error) {
-
-	var err error
-	f := &Fetcher{
-		Params:    params,
-		Callbacks: clbks,
-		Client:    client,
-		caller:    component,
-	}
-	if f.Pool, err = discoverypool.New(svcInfo); err != nil {
-		return nil, err
-	}
-	return f, nil
-}
-
-// UpdateInstances updates the discovery service pool.
-func (f *Fetcher) UpdateInstances(svcInfo topology.IDAddrMap) error {
-	return f.Pool.Update(svcInfo)
-}
-
-// Name returns the tasks name.
-func (f *Fetcher) Name() string {
-	return fmt.Sprintf("%s_discovery_fetcher", f.caller)
-}
-
-// Run fetches a new topology file from the discovery service and calls the
-// appropriate callback functions to notify the caller. RawF and UpdateF are
-// only called if no error has occurred and the topology was parsed correctly.
-// Otherwise ErrorF is called.
-func (f *Fetcher) Run(ctx context.Context) {
-	if err := f.run(ctx); err != nil && f.Callbacks.Error != nil {
-		f.Callbacks.Error(ctx, err)
-	}
-}
-
-func (f *Fetcher) run(ctx context.Context) error {
-	if f.Pool == nil {
-		return serrors.New("Pool not initialized")
-	}
-	// Choose a DS server.
-	ds, err := f.Pool.Choose()
-	if err != nil {
-		return err
-	}
-	topo, raw, err := discovery.FetchTopoRaw(ctx, f.Params, ds.Addr(), f.Client)
-	if err != nil {
-		ds.Fail()
-		return err
-	}
-	// Update DS server entries based on new topo.
-	err = f.Pool.Update(topo.DS)
-	if err != nil {
-		return common.NewBasicError("Unable to update pool", err)
-	}
-	// Notify the client.
-	if f.Callbacks.Raw != nil {
-		f.Callbacks.Raw(ctx, raw, topo)
-	}
-	if f.Callbacks.Update != nil {
-		f.Callbacks.Update(ctx, topo)
-	}
-	return nil
-}
diff --git a/go/lib/env/env.go b/go/lib/env/env.go
index e43d9717c..64189d466 100644
--- a/go/lib/env/env.go
+++ b/go/lib/env/env.go
@@ -201,7 +201,7 @@ func ReloadTopology(topologyPath string) {
 		log.Error("Unable to reload topology", "err", err)
 		return
 	}
-	if _, _, err := itopo.SetStatic(topo, true); err != nil {
+	if _, _, err := itopo.SetStatic(topo); err != nil {
 		log.Error("Unable to set topology", "err", err)
 		return
 	}
diff --git a/go/lib/healthpool/BUILD.bazel b/go/lib/healthpool/BUILD.bazel
deleted file mode 100644
index b75ebc24a..000000000
--- a/go/lib/healthpool/BUILD.bazel
+++ /dev/null
@@ -1,32 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
-
-go_library(
-    name = "go_default_library",
-    srcs = [
-        "doc.go",
-        "info.go",
-        "opts.go",
-        "pool.go",
-    ],
-    importpath = "github.com/scionproto/scion/go/lib/healthpool",
-    visibility = ["//visibility:public"],
-    deps = [
-        "//go/lib/common:go_default_library",
-        "//go/lib/periodic:go_default_library",
-        "//go/lib/serrors:go_default_library",
-    ],
-)
-
-go_test(
-    name = "go_default_test",
-    srcs = [
-        "info_test.go",
-        "pool_test.go",
-    ],
-    embed = [":go_default_library"],
-    deps = [
-        "@com_github_smartystreets_goconvey//convey:go_default_library",
-        "@com_github_stretchr_testify//assert:go_default_library",
-        "@com_github_stretchr_testify//require:go_default_library",
-    ],
-)
diff --git a/go/lib/healthpool/doc.go b/go/lib/healthpool/doc.go
deleted file mode 100644
index 16bc936e9..000000000
--- a/go/lib/healthpool/doc.go
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2019 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package healthpool provides a generic way to keep track of the health
-// infos for a set of keys.
-//
-// Usage
-//
-// This package is used to implement health pools for specific purposes.
-// They can be found in the subpackages. Client packages should use these
-// implementations, unless they implement their own specific health pool.
-//
-// Pool
-//
-// The pool keeps a map of all registered keys to their health info. It is
-// used to choose the best info based on the fail count and the initialized
-// selection algorithm. The behavior of the pool can be modified at
-// initialization with the provided PoolOptions.
-//
-// The pool periodically reduces the fail count for every info that has not
-// failed for a specified amount of time. The fail count is divided by two
-// every expire interval starting from that point.
-//
-// Info
-//
-// The info keeps track of the failures for a given key. The client should
-// call the Fail method to increase the fail count.
-package healthpool
diff --git a/go/lib/healthpool/info.go b/go/lib/healthpool/info.go
deleted file mode 100644
index cec9b5cde..000000000
--- a/go/lib/healthpool/info.go
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2019 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package healthpool
-
-import (
-	"math"
-	"sync"
-	"time"
-)
-
-// MaxFailCount is the maximum fail count for a health info.
-const MaxFailCount = math.MaxUint16
-
-// Info keeps track of the fails for a key. Implementations that want to
-// use healthpool should embed this interface and initialize it with the
-// constructor NewInfo. See healthpool/svcinstance for an example.
-type Info interface {
-	// Fail increases the fail count.
-	Fail()
-	// FailCount returns the fail count.
-	FailCount() int
-	// ResetCount resets the fail count to zero.
-	ResetCount()
-	// expireFails reduces the fail count.
-	expireFails(now time.Time, opts ExpireOptions)
-}
-
-var _ Info = (*info)(nil)
-
-// info is the private implementation for Info.
-type info struct {
-	mtx      sync.RWMutex
-	lastFail time.Time
-	lastExp  time.Time
-	fails    uint16
-}
-
-// NewInfo creates a new health info.
-func NewInfo() Info {
-	return &info{
-		lastFail: time.Now(),
-		lastExp:  time.Now(),
-	}
-}
-
-func (c *info) Fail() {
-	c.mtx.Lock()
-	defer c.mtx.Unlock()
-	c.lastFail = time.Now()
-	if int(c.fails) < MaxFailCount {
-		c.fails++
-	}
-}
-
-func (c *info) FailCount() int {
-	c.mtx.RLock()
-	defer c.mtx.RUnlock()
-	return int(c.fails)
-}
-
-func (c *info) ResetCount() {
-	c.mtx.Lock()
-	defer c.mtx.Unlock()
-	c.fails = 0
-}
-
-// expireFails exponentially reduces the fail count.
-func (c *info) expireFails(now time.Time, opts ExpireOptions) {
-	c.mtx.Lock()
-	defer c.mtx.Unlock()
-	expStart := max(c.lastFail.Add(opts.start()), c.lastExp.Add(opts.interval()))
-	if now.Before(expStart) {
-		return
-	}
-	c.fails >>= uint(now.Sub(expStart)/opts.interval()) + 1
-	c.lastExp = now
-}
-
-func max(a, b time.Time) time.Time {
-	if a.After(b) {
-		return a
-	}
-	return b
-}
diff --git a/go/lib/healthpool/opts.go b/go/lib/healthpool/opts.go
deleted file mode 100644
index 79f3d00d2..000000000
--- a/go/lib/healthpool/opts.go
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2019 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package healthpool
-
-import "time"
-
-// PoolOptions define the behavior of the pool.
-type PoolOptions struct {
-	// Algorithm is the choosing algorithm. In case of the empty string,
-	// MinFailCount is used.
-	Algorithm Algorithm
-	// AllowEmpty indicates that the pool is allowed to be empty.
-	AllowEmpty bool
-	// Expire contains the expiration options.
-	Expire ExpireOptions
-}
-
-func (opts PoolOptions) algorithm(p *Pool) func() (Info, error) {
-	switch opts.Algorithm {
-	case "", MinFailCount:
-		return p.chooseMinFails
-	default:
-		return nil
-	}
-}
-
-const (
-	// MinFailCount selects a pool entry with the minimum fail count.
-	MinFailCount Algorithm = "MinFailCount"
-)
-
-// Algorithm is the choosing algorithm of the pool.
-type Algorithm string
-
-const (
-	// DefaultExpireStart is the the default for ExpireStart.
-	DefaultExpireStart = 5 * time.Minute
-	// DefaultExpireInterval is the default for ExpireInterval.
-	DefaultExpireInterval = 10 * time.Second
-)
-
-// ExpireOptions define the expiration behavior.
-type ExpireOptions struct {
-	// Start is the time without failures for an info after which
-	// exponential fail expiration starts. In case of the zero value, the
-	// DefaultExpireStart is used.
-	Start time.Duration
-	// Interval is the time between exponential fail expirations.
-	// In case of the zero value, the DefaultExpireInterval is used.
-	Interval time.Duration
-}
-
-func (opts *ExpireOptions) start() time.Duration {
-	return defaultDur(opts.Start, DefaultExpireStart)
-}
-
-func (opts *ExpireOptions) interval() time.Duration {
-	return defaultDur(opts.Interval, DefaultExpireInterval)
-}
-
-func defaultDur(val, def time.Duration) time.Duration {
-	if val == 0 {
-		return def
-	}
-	return val
-}
diff --git a/go/lib/healthpool/pool.go b/go/lib/healthpool/pool.go
deleted file mode 100644
index 85c65425a..000000000
--- a/go/lib/healthpool/pool.go
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright 2019 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package healthpool
-
-import (
-	"context"
-	"sync"
-	"time"
-
-	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/periodic"
-	"github.com/scionproto/scion/go/lib/serrors"
-)
-
-// ErrPoolClosed is the error returned when operations on a closed pool are
-// executed.
-const ErrPoolClosed common.ErrMsg = "Pool closed"
-
-// InfoSet is a set of infos.
-type InfoSet map[Info]struct{}
-
-// List transforms the info set to a list.
-func (s InfoSet) List() []Info {
-	infos := make([]Info, 0, len(s))
-	for info := range s {
-		infos = append(infos, info)
-	}
-	return infos
-}
-
-// Pool holds entries and their health. It allows to choose entries based
-// on their health.
-type Pool struct {
-	infosMtx sync.RWMutex
-	infos    map[Info]Info
-	choose   func() (Info, error)
-	opts     PoolOptions
-	expirer  *periodic.Runner
-	closed   bool
-}
-
-// NewPool creates a health pool that contains all entries provided in infos.
-func NewPool(infos map[Info]struct{}, opts PoolOptions) (*Pool, error) {
-	p := &Pool{
-		infos: make(map[Info]Info, len(infos)),
-		opts:  opts,
-	}
-	if p.choose = opts.algorithm(p); p.choose == nil {
-		return nil, common.NewBasicError("Invalid algorithm", nil, "algo", opts.Algorithm)
-	}
-	if err := p.Update(infos); err != nil {
-		return nil, err
-	}
-	//TODO(karampok). Before we start the task, we need caller identifier (e.g. bs_healthpool_x)
-	p.expirer = periodic.Start((*expirer)(p), time.Second, time.Second)
-	return p, nil
-}
-
-// Update updates the info entries in the pool. Entries in the pool that
-// are not in infos are removed. Entries in infos that are not in the pool
-// are added. However, if Options.AllowEmpty is not set, and the Update
-// causes an empty pool, the entries are not replaced and an error is
-// returned. If the pool is closed, an error is returned.
-func (p *Pool) Update(infos map[Info]struct{}) error {
-	if len(infos) == 0 && !p.opts.AllowEmpty {
-		return common.NewBasicError("Info must contain entry", nil, "opts", p.opts)
-	}
-	p.infosMtx.Lock()
-	defer p.infosMtx.Unlock()
-	if p.closed {
-		return common.NewBasicError(ErrPoolClosed, nil)
-	}
-	for info := range infos {
-		p.infos[info] = info
-	}
-	// Remove infos that are no longer present.
-	for info := range p.infos {
-		if _, ok := infos[info]; !ok {
-			delete(p.infos, info)
-		}
-	}
-	return nil
-}
-
-// Choose chooses info based on the configured algorithm. If the pool is
-// closed, an error is returned.
-func (p *Pool) Choose() (Info, error) {
-	p.infosMtx.RLock()
-	defer p.infosMtx.RUnlock()
-	if p.closed {
-		return nil, common.NewBasicError(ErrPoolClosed, nil)
-	}
-	return p.choose()
-}
-
-// Infos returns a list of all infos in the pool.
-func (p *Pool) Infos() []Info {
-	p.infosMtx.RLock()
-	defer p.infosMtx.RUnlock()
-	infos := make([]Info, 0, len(p.infos))
-	for info := range p.infos {
-		infos = append(infos, info)
-	}
-	return infos
-}
-
-// Close closes the pool and stops the periodic fail expiration. After
-// closing the pool, Update and Choose will return errors. The pool is safe
-// to being closed multiple times.
-func (p *Pool) Close() {
-	p.infosMtx.Lock()
-	defer p.infosMtx.Unlock()
-	if !p.closed {
-		p.closed = true
-		p.expirer.Stop()
-	}
-}
-
-// chooseMinFails is a choosing algorithm which returns a info with minimum
-// fail count.
-func (p *Pool) chooseMinFails() (Info, error) {
-	var best Info
-	var minFail = -1
-	for _, info := range p.infos {
-		failCount := info.FailCount()
-		if minFail == -1 || failCount < minFail {
-			best = info
-			minFail = failCount
-		}
-	}
-	if best == nil {
-		return nil, serrors.New("Unable to find an info instance")
-	}
-	return best, nil
-}
-
-// expirer is a wrapper to implement period.Task.
-type expirer Pool
-
-func (e *expirer) Name() string {
-	return "healthpool_expirer"
-}
-
-func (e *expirer) Run(_ context.Context) {
-	p := (*Pool)(e)
-	p.infosMtx.RLock()
-	defer p.infosMtx.RUnlock()
-	now := time.Now()
-	for _, info := range p.infos {
-		info.expireFails(now, p.opts.Expire)
-	}
-}
diff --git a/go/lib/healthpool/svcinstance/BUILD.bazel b/go/lib/healthpool/svcinstance/BUILD.bazel
deleted file mode 100644
index 40852c341..000000000
--- a/go/lib/healthpool/svcinstance/BUILD.bazel
+++ /dev/null
@@ -1,28 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
-
-go_library(
-    name = "go_default_library",
-    srcs = [
-        "info.go",
-        "pool.go",
-    ],
-    importpath = "github.com/scionproto/scion/go/lib/healthpool/svcinstance",
-    visibility = ["//visibility:public"],
-    deps = [
-        "//go/lib/healthpool:go_default_library",
-        "//go/lib/topology:go_default_library",
-    ],
-)
-
-go_test(
-    name = "go_default_test",
-    srcs = ["pool_test.go"],
-    data = glob(["testdata/**"]),
-    embed = [":go_default_library"],
-    deps = [
-        "//go/lib/healthpool:go_default_library",
-        "//go/lib/topology:go_default_library",
-        "//go/lib/xtest:go_default_library",
-        "@com_github_smartystreets_goconvey//convey:go_default_library",
-    ],
-)
diff --git a/go/lib/healthpool/svcinstance/info.go b/go/lib/healthpool/svcinstance/info.go
deleted file mode 100644
index e58f3d8b6..000000000
--- a/go/lib/healthpool/svcinstance/info.go
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2019 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package svcinstance
-
-import (
-	"net"
-	"sync"
-
-	"github.com/scionproto/scion/go/lib/healthpool"
-)
-
-// Info holds the information for a service instance.
-type Info struct {
-	info *info
-}
-
-// Fail increases the fail count. It shall be called when a request to the
-// service instance fails.
-func (i Info) Fail() {
-	i.info.Fail()
-}
-
-// Addr returns the service instance address.
-func (i Info) Addr() *net.UDPAddr {
-	return i.info.addrCopy()
-}
-
-// Name returns the service instance name.
-func (i Info) Name() string {
-	return i.info.name
-}
-
-type info struct {
-	healthpool.Info
-	mtx  sync.RWMutex
-	addr *net.UDPAddr
-	name string
-}
-
-func (i *info) addrCopy() *net.UDPAddr {
-	i.mtx.RLock()
-	defer i.mtx.RUnlock()
-	return &net.UDPAddr{
-		IP:   append(i.addr.IP[:0:0], i.addr.IP...),
-		Port: i.addr.Port,
-	}
-
-}
-
-func (i *info) update(a *net.UDPAddr) {
-	i.mtx.Lock()
-	defer i.mtx.Unlock()
-	if !a.IP.Equal(i.addr.IP) || a.Port != i.addr.Port {
-		i.addr = a
-		i.ResetCount()
-	}
-}
diff --git a/go/lib/healthpool/svcinstance/pool.go b/go/lib/healthpool/svcinstance/pool.go
deleted file mode 100644
index ac05b73f2..000000000
--- a/go/lib/healthpool/svcinstance/pool.go
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2019 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package svcinstance provides a pool to keep track of the health status
-// of service instances.
-//
-// Usage
-//
-// Instantiate the pool with a set of service instances. Use choose to
-// select the best info according to the specified choosing algorithm. The
-// caller should keep a reference to the returned Info and call Fail if an
-// error is encountered during the request.
-package svcinstance
-
-import (
-	"sync"
-
-	"github.com/scionproto/scion/go/lib/healthpool"
-	"github.com/scionproto/scion/go/lib/topology"
-)
-
-type infoMap map[string]*info
-
-func (m infoMap) toSet() healthpool.InfoSet {
-	infos := make(healthpool.InfoSet, len(m))
-	for _, v := range m {
-		infos[v] = struct{}{}
-	}
-	return infos
-}
-
-// Pool keeps track of the service instances information and their health
-// status. It allows to choose service instances based on their health.
-type Pool struct {
-	mtx   sync.Mutex
-	infos infoMap
-	hpool *healthpool.Pool
-}
-
-// NewPool initializes the pool with the provided service instances and pool options.
-func NewPool(svcInfo topology.IDAddrMap, opts healthpool.PoolOptions) (*Pool, error) {
-	p := &Pool{
-		infos: createMap(svcInfo, nil),
-	}
-	var err error
-	if p.hpool, err = healthpool.NewPool(p.infos.toSet(), opts); err != nil {
-		return nil, err
-	}
-	return p, nil
-}
-
-// Update updates the service instances in the pool. Instances in the pool
-// that are not in svcInfo are removed. Instances in svcInfo that are not
-// in the pool are added. Changed addresses of existing instances are
-// updated. In that case, the fail count is reset to zero. However, if
-// Options.AllowEmpty is not set, and the Update causes an empty pool, the
-// instances are not replaced and an error is returned. If the pool is
-// closed, an error is returned.
-func (p *Pool) Update(svcInfo topology.IDAddrMap) error {
-	p.mtx.Lock()
-	defer p.mtx.Unlock()
-	infos := createMap(svcInfo, p.infos)
-	if err := p.hpool.Update(infos.toSet()); err != nil {
-		return err
-	}
-	p.infos = infos
-	return nil
-}
-
-// Choose chooses the instance based on the configured algorithm. If the
-// pool is closed, an error is returned.
-func (p *Pool) Choose() (Info, error) {
-	hinfo, err := p.hpool.Choose()
-	if err != nil {
-		return Info{}, err
-	}
-	return Info{info: hinfo.(*info)}, nil
-}
-
-// Close closes the pool. After closing the pool, Update and Choose will
-// return errors. The pool is safe to being closed multiple times.
-func (p *Pool) Close() {
-	p.hpool.Close()
-}
-
-func createMap(svcInfo topology.IDAddrMap, oldInfos infoMap) infoMap {
-	infos := make(infoMap, len(svcInfo))
-	for k, svc := range svcInfo {
-		if oldInfo, ok := oldInfos[k]; ok {
-			infos[k] = oldInfo
-			oldInfo.update(svc.SCIONAddress)
-		} else {
-			infos[k] = &info{
-				Info: healthpool.NewInfo(),
-				addr: svc.SCIONAddress,
-				name: k,
-			}
-		}
-	}
-	return infos
-}
diff --git a/go/lib/infra/modules/idiscovery/BUILD.bazel b/go/lib/infra/modules/idiscovery/BUILD.bazel
deleted file mode 100644
index b2bcb4d5e..000000000
--- a/go/lib/infra/modules/idiscovery/BUILD.bazel
+++ /dev/null
@@ -1,26 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-
-go_library(
-    name = "go_default_library",
-    srcs = [
-        "config.go",
-        "idiscovery.go",
-        "sample.go",
-    ],
-    importpath = "github.com/scionproto/scion/go/lib/infra/modules/idiscovery",
-    visibility = ["//visibility:public"],
-    deps = [
-        "//go/lib/common:go_default_library",
-        "//go/lib/config:go_default_library",
-        "//go/lib/discovery:go_default_library",
-        "//go/lib/discovery/topofetcher:go_default_library",
-        "//go/lib/fatal:go_default_library",
-        "//go/lib/infra/modules/idiscovery/internal/metrics:go_default_library",
-        "//go/lib/infra/modules/itopo:go_default_library",
-        "//go/lib/log:go_default_library",
-        "//go/lib/periodic:go_default_library",
-        "//go/lib/serrors:go_default_library",
-        "//go/lib/topology:go_default_library",
-        "//go/lib/util:go_default_library",
-    ],
-)
diff --git a/go/lib/infra/modules/idiscovery/config.go b/go/lib/infra/modules/idiscovery/config.go
deleted file mode 100644
index d516175aa..000000000
--- a/go/lib/infra/modules/idiscovery/config.go
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright 2019 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package idiscovery
-
-import (
-	"io"
-	"strings"
-	"time"
-
-	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/config"
-	"github.com/scionproto/scion/go/lib/serrors"
-	"github.com/scionproto/scion/go/lib/util"
-)
-
-var (
-	// DefaultDynamicFetchInterval is the default time between two dynamic topology queries.
-	DefaultDynamicFetchInterval = 5 * time.Second
-	// DefaultStaticFetchInterval is the default time between two static topology queries.
-	DefaultStaticFetchInterval = 5 * time.Minute
-	// DefaultFetchTimeout is the default timeout for a query.
-	DefaultFetchTimeout = 1 * time.Second
-	// DefaultInitialConnectPeriod is the default total amount of time spent attempting
-	// to connect to the discovery service on start.
-	DefaultInitialConnectPeriod = 20 * time.Second
-)
-
-var _ config.Config = (*Config)(nil)
-
-type Config struct {
-	// Static contains the parameters for fetching the static
-	// topology from the discovery service.
-	Static StaticConfig
-	// Dynamic contains the parameters for fetching the dynamic
-	// topology from the discovery service.
-	Dynamic FetchConfig
-}
-
-func (cfg *Config) InitDefaults() {
-	config.InitAll(&cfg.Static, &cfg.Dynamic)
-}
-
-func (cfg *Config) Validate() error {
-	return config.ValidateAll(&cfg.Static, &cfg.Dynamic)
-}
-
-func (cfg *Config) Sample(dst io.Writer, path config.Path, ctx config.CtxMap) {
-	config.WriteSample(dst, path, ctx, &cfg.Static, &cfg.Dynamic)
-}
-
-func (cfg *Config) ConfigName() string {
-	return "discovery"
-}
-
-var _ config.Config = (*StaticConfig)(nil)
-
-type StaticConfig struct {
-	FetchConfig
-	// Filename indicates the file that the static topology is written to on updates.
-	// The empty string indicates that the static topology is not written.
-	Filename string
-}
-
-func (cfg *StaticConfig) InitDefaults() {
-	cfg.Connect.InitDefaults()
-	if cfg.Interval.Duration == 0 {
-		cfg.Interval.Duration = DefaultStaticFetchInterval
-	}
-	if cfg.Timeout.Duration == 0 {
-		cfg.Timeout.Duration = DefaultFetchTimeout
-	}
-}
-
-func (cfg *StaticConfig) Sample(dst io.Writer, path config.Path, ctx config.CtxMap) {
-	config.WriteString(dst, staticSample)
-	config.WriteSample(dst, path, ctx, &cfg.Connect)
-}
-
-func (cfg *StaticConfig) ConfigName() string {
-	return "static"
-}
-
-var _ config.Config = (*FetchConfig)(nil)
-
-type FetchConfig struct {
-	// Enable indicates whether the discovery service is queried
-	// for updated topologies.
-	Enable bool
-	// Interval specifies the time between two queries.
-	Interval util.DurWrap
-	// Timeout specifies the timeout for a single query.
-	Timeout util.DurWrap
-	// Https indicates whether https must be used to fetch the topology.
-	Https bool
-	// Connect contains the parameters for the initial connection
-	// check to the discovery service.
-	Connect ConnectParams
-}
-
-func (cfg *FetchConfig) InitDefaults() {
-	cfg.Connect.InitDefaults()
-	if cfg.Interval.Duration == 0 {
-		cfg.Interval.Duration = DefaultDynamicFetchInterval
-	}
-	if cfg.Timeout.Duration == 0 {
-		cfg.Timeout.Duration = DefaultFetchTimeout
-	}
-}
-
-func (cfg *FetchConfig) Validate() error {
-	if cfg.Interval.Duration == 0 {
-		return serrors.New("Interval must not be zero")
-	}
-	if cfg.Timeout.Duration == 0 {
-		return serrors.New("Timeout must not be zero")
-	}
-	return cfg.Connect.Validate()
-}
-
-func (cfg *FetchConfig) Sample(dst io.Writer, path config.Path, ctx config.CtxMap) {
-	config.WriteString(dst, dynamicSample)
-	config.WriteSample(dst, path, ctx, &cfg.Connect)
-}
-
-func (cfg *FetchConfig) ConfigName() string {
-	return "dynamic"
-}
-
-var _ config.Config = (*ConnectParams)(nil)
-
-type ConnectParams struct {
-	// InitialPeriod indicates for how long the process tries to get a valid
-	// response from the discovery service until FailAction is executed.
-	InitialPeriod util.DurWrap
-	// FailAction indicates the action that should be taken if no topology can
-	// be fetched from the discovery service within the InitialPeriod.
-	FailAction FailAction
-}
-
-func (cfg *ConnectParams) InitDefaults() {
-	if cfg.InitialPeriod.Duration == 0 {
-		cfg.InitialPeriod.Duration = DefaultInitialConnectPeriod
-	}
-	if cfg.FailAction != FailActionFatal {
-		cfg.FailAction = FailActionContinue
-	}
-}
-
-func (cfg *ConnectParams) Validate() error {
-	if cfg.InitialPeriod.Duration == 0 {
-		return serrors.New("InitialPeriod must not be zero")
-	}
-	return cfg.FailAction.Validate()
-}
-
-func (cfg *ConnectParams) Sample(dst io.Writer, path config.Path, _ config.CtxMap) {
-	config.WriteString(dst, connectSample)
-}
-
-func (cfg *ConnectParams) ConfigName() string {
-	return "connect"
-}
-
-type FailAction string
-
-const (
-	// FailActionFatal indicates that the process exits on error.
-	FailActionFatal FailAction = "Fatal"
-	// FailActionContinue indicates that the process continues on error.
-	FailActionContinue FailAction = "Continue"
-)
-
-func (f *FailAction) Validate() error {
-	switch *f {
-	case FailActionContinue, FailActionFatal:
-		return nil
-	default:
-		return common.NewBasicError("Unknown FailAction", nil, "input", string(*f))
-	}
-}
-
-func (f *FailAction) UnmarshalText(text []byte) error {
-	switch strings.ToLower(string(text)) {
-	case strings.ToLower(string(FailActionFatal)):
-		*f = FailActionFatal
-	case strings.ToLower(string(FailActionContinue)):
-		*f = FailActionContinue
-	default:
-		return common.NewBasicError("Unknown FailAction", nil, "input", string(text))
-	}
-	return nil
-}
diff --git a/go/lib/infra/modules/idiscovery/idiscovery.go b/go/lib/infra/modules/idiscovery/idiscovery.go
deleted file mode 100644
index f81f21716..000000000
--- a/go/lib/infra/modules/idiscovery/idiscovery.go
+++ /dev/null
@@ -1,407 +0,0 @@
-// Copyright 2019 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package idiscovery fetches the topology from the discovery service.
-//
-// Client packages can start a periodic.Runner with StartRunners that
-// periodically fetches the static and dynamic topology from the discovery
-// service. The received topology is set in itopo.
-//
-// Initially, the runners try to fetch the topology every second for the
-// configured InitialPeriod until a fetch succeeded. If no fetch is successful
-// after the InitialPeriod, the FailAction is taken. 'Fatal' causes the process
-// to exit. 'Continue' logs a warning, the process continues its execution.
-//
-// By default changes to the semi-mutable section of static topologies is
-// not allowed. It can be enabled by providing a custom topo handler.
-//
-// The periodic.Runner for the static topology can be instructed to
-// write updated versions to the file system. To enable this, set
-// the filename in StaticConfig.
-//
-// A periodic.Task with a customized TopoHandler can be created with
-// NewFetcher, when the client package requires more control.
-package idiscovery
-
-import (
-	"context"
-	"fmt"
-	"net/http"
-	"sync"
-	"time"
-
-	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/discovery"
-	"github.com/scionproto/scion/go/lib/discovery/topofetcher"
-	"github.com/scionproto/scion/go/lib/fatal"
-	"github.com/scionproto/scion/go/lib/infra/modules/idiscovery/internal/metrics"
-	"github.com/scionproto/scion/go/lib/infra/modules/itopo"
-	"github.com/scionproto/scion/go/lib/log"
-	"github.com/scionproto/scion/go/lib/periodic"
-	"github.com/scionproto/scion/go/lib/serrors"
-	"github.com/scionproto/scion/go/lib/topology"
-	"github.com/scionproto/scion/go/lib/util"
-)
-
-// TopoHandler handles a topology fetched from the discovery service, and
-// returns whether the provided topology is an update.
-type TopoHandler func(topo *topology.RWTopology) (bool, error)
-
-func staticSetter(topo *topology.RWTopology) (bool, error) {
-	_, updated, err := itopo.SetStatic(topology.FromRWTopology(topo), false)
-	return updated, err
-}
-
-// dynamicSetter sets the dynamic topology in itopo with the provided topology.
-func dynamicSetter(topo *topology.RWTopology) (bool, error) {
-	_, updated, err := itopo.SetDynamic(topo)
-	return updated, err
-}
-
-// TopoHandlers contains custom topology handlers.
-type TopoHandlers struct {
-	// Static handles the static topology.
-	Static TopoHandler
-	// Dynamic handles the dynamic topology.
-	Dynamic TopoHandler
-}
-
-func (t *TopoHandlers) static() TopoHandler {
-	if t.Static != nil {
-		return t.Static
-	}
-	return staticSetter
-}
-
-func (t *TopoHandlers) dynamic() TopoHandler {
-	if t.Dynamic != nil {
-		return t.Dynamic
-	}
-	return dynamicSetter
-}
-
-type Runners struct {
-	// Static periodically fetches the static topology and sets it in itopo.
-	Static *Runner
-	// Dynamic periodically fetches the dynamic topology and sets it in itopo.
-	Dynamic *Runner
-	// Cleaner periodically cleans the expired dynamic topology in itopo.
-	Cleaner *periodic.Runner
-}
-
-// StartRunners starts the runners for the specified configuration. In case the topo handler
-// function is not set, StartRunners defaults to setting the topology in itopo.
-func StartRunners(cfg Config, file discovery.File, handlers TopoHandlers,
-	client *http.Client, caller string) (Runners, error) {
-
-	cfg.InitDefaults()
-	var err error
-	r := Runners{}
-	if cfg.Static.Enable {
-		r.Static, err = startPeriodicFetcher(
-			cfg.Static.FetchConfig,
-			handlers.static(),
-			discovery.FetchParams{
-				Mode:  discovery.Static,
-				Https: cfg.Static.Https,
-				File:  file,
-			},
-			cfg.Static.Filename,
-			client,
-			caller,
-		)
-		if err != nil {
-			return Runners{}, err
-		}
-	}
-	if cfg.Dynamic.Enable {
-		r.Dynamic, err = startPeriodicFetcher(
-			cfg.Dynamic,
-			handlers.dynamic(),
-			discovery.FetchParams{
-				Mode:  discovery.Dynamic,
-				Https: cfg.Dynamic.Https,
-				File:  file,
-			},
-			"",
-			client,
-			caller,
-		)
-		if err != nil {
-			r.Kill()
-			return Runners{}, err
-		}
-		r.Cleaner = itopo.StartCleaner(1*time.Second, 1*time.Second, caller)
-		log.Info("[idiscovery] Started dynamic topology fetcher")
-	}
-	return r, nil
-}
-
-// Kill kills all runners.
-func (r *Runners) Kill() {
-	if r.Static != nil {
-		r.Static.Kill()
-	}
-	if r.Dynamic != nil {
-		r.Dynamic.Kill()
-	}
-	if r.Cleaner != nil {
-		r.Cleaner.Kill()
-	}
-}
-
-// Runner periodically fetches the topology from the discovery service. On start up,
-// every second a request is sent, until the configured initial period has passed,
-// or a topology has been fetched successfully.
-type Runner struct {
-	fetcherMtx sync.Mutex
-	// fetcher periodically fetches the topology.
-	fetcher *periodic.Runner
-	// rawHandler is the handler provided by the caller.
-	rawHandler TopoHandler
-	// stop is a channel to signal the initial period go routine to stop.
-	stop chan struct{}
-	// stopping indicates that the stopping process has started.
-	stopping bool
-	// fetched is used to communicate a successfully fetched topology.
-	fetched flag
-}
-
-// Kill stops the periodic execution of the Runner.
-func (r *Runner) Kill() {
-	r.fetcherMtx.Lock()
-	defer r.fetcherMtx.Unlock()
-	r.stopping = true
-	close(r.stop)
-	if r.fetcher != nil {
-		r.fetcher.Kill()
-	}
-}
-
-// startPeriodicFetcher starts a runner that periodically fetches the topology.
-// If during the InitialPeriod no topology is successfully fetched, the process takes
-// the configured FailAction.
-func startPeriodicFetcher(cfg FetchConfig, handler TopoHandler, params discovery.FetchParams,
-	filename string, client *http.Client, caller string) (*Runner, error) {
-
-	fatal.Check()
-	r := &Runner{
-		rawHandler: handler,
-		stop:       make(chan struct{}),
-		fetched: flag{
-			c: make(chan struct{}),
-		},
-	}
-	fetcher, err := NewFetcher(r.handler, params, filename, client, caller)
-	if err != nil {
-		return nil, err
-	}
-	r.startInitialPeriod(fetcher, cfg)
-	return r, nil
-}
-
-// startInitialPeriod fetches the topology every second for the initial period,
-// or until the a topology has been fetched successfully.
-func (r *Runner) startInitialPeriod(fetcher *task, cfg FetchConfig) {
-	go func() {
-		defer log.LogPanicAndExit()
-		ticker := time.NewTicker(time.Second)
-		initialPeriod := time.NewTimer(cfg.Connect.InitialPeriod.Duration)
-		defer ticker.Stop()
-		defer initialPeriod.Stop()
-		r.startFetch(fetcher, cfg.Timeout.Duration)
-		for {
-			select {
-			case <-r.stop:
-				return
-			case <-initialPeriod.C:
-				r.execFailAction(fetcher, cfg)
-				return
-			case <-r.fetched.c:
-				r.startRegularFetcher(fetcher, cfg)
-				return
-			case <-ticker.C:
-				r.startFetch(fetcher, cfg.Timeout.Duration)
-			}
-		}
-	}()
-}
-
-// execFailAction executes the FailAction.
-func (r *Runner) execFailAction(fetcher *task, cfg FetchConfig) {
-	switch cfg.Connect.FailAction {
-	case FailActionContinue:
-		log.Warn("[discovery] Unable to get a valid initial topology, ignoring")
-		r.startRegularFetcher(fetcher, cfg)
-	default:
-		fatal.Fatal(serrors.New("Unable to get a valid initial topology"))
-	}
-}
-
-// startRegularFetcher starts a periodic fetcher with the configured interval and timeout.
-// If the runner is in the process of stopping, no fetcher is started.
-func (r *Runner) startRegularFetcher(fetcher *task, cfg FetchConfig) {
-	r.fetcherMtx.Lock()
-	defer r.fetcherMtx.Unlock()
-	if r.stopping {
-		return
-	}
-	r.fetcher = periodic.Start(fetcher, cfg.Interval.Duration,
-		cfg.Timeout.Duration)
-}
-
-// startFetch starts a go routine that executes the fetch task.
-func (r *Runner) startFetch(fetcher *task, timeout time.Duration) {
-	go func() {
-		defer log.LogPanicAndExit()
-		ctx, cancel := context.WithTimeout(context.Background(), timeout)
-		defer cancel()
-		fetcher.Run(ctx)
-	}()
-}
-
-// handler wraps the handler provided by the caller and sets the set flag if a topology
-// is handled successfully without error.
-func (r *Runner) handler(topo *topology.RWTopology) (bool, error) {
-	updated, err := r.rawHandler(topo)
-	if err != nil {
-		return updated, err
-	}
-	r.fetched.Set()
-	return updated, err
-}
-
-// task is a periodic.Task that fetches the topology from the discovery service.
-type task struct {
-	mode     discovery.Mode
-	handler  TopoHandler
-	fetcher  *topofetcher.Fetcher
-	filename string
-	caller   string
-}
-
-// NewFetcher creates a periodic.Task that fetches the topology from the discovery
-// service and calls the provided handler on the received topology. If the handler
-// indicates an update, and filename is set, the topology is written.
-func NewFetcher(handler TopoHandler, params discovery.FetchParams,
-	filename string, client *http.Client, caller string) (*task, error) {
-
-	if handler == nil {
-		return nil, serrors.New("handler must not be nil")
-	}
-	t := &task{
-		mode:     params.Mode,
-		handler:  handler,
-		filename: filename,
-		caller:   caller,
-	}
-	var err error
-	t.fetcher, err = topofetcher.New(
-		itopo.Get().DS(),
-		params,
-		topofetcher.Callbacks{
-			Error: t.handleErr,
-			Raw:   t.handleRaw,
-		},
-		client,
-		caller,
-	)
-	if err != nil {
-		return nil, common.NewBasicError("Unable to initialize fetcher", err)
-	}
-	return t, nil
-}
-
-func (t *task) Name() string {
-	return fmt.Sprintf("%s_discovery_fetcher_%v", t.caller, t.mode)
-}
-
-func (t *task) Run(ctx context.Context) {
-	if err := t.fetcher.UpdateInstances(itopo.Get().DS()); err != nil {
-		t.logger(ctx).Error("[discovery] Unable to update instances", "err", err)
-		return
-	}
-	t.fetcher.Run(ctx)
-}
-
-func (t *task) handleErr(ctx context.Context, err error) {
-	t.logger(ctx).Error("[discovery] Unable to fetch topology", "err", err)
-	l := metrics.FetcherLabels{Static: t.static(), Result: metrics.ErrRequest}
-	metrics.Fetcher.Sent(l).Inc()
-}
-
-func (t *task) handleRaw(ctx context.Context, raw common.RawBytes, topo *topology.RWTopology) {
-	l := metrics.FetcherLabels{Static: t.static(), Result: metrics.Success}
-	updated, err := t.callHandler(ctx, topo)
-	switch {
-	case err != nil:
-		l.Result = metrics.ErrUpdate
-	case !updated:
-		l.Result = metrics.OkIgnored
-	}
-	if err != nil || t.filename == "" || !updated {
-		metrics.Fetcher.Sent(l).Inc()
-		return
-	}
-	if err := t.writeFile(ctx, raw); err != nil {
-		l.Result = metrics.ErrWriteFile
-	}
-	metrics.Fetcher.Sent(l).Inc()
-}
-
-func (t *task) writeFile(ctx context.Context, raw common.RawBytes) error {
-	l := metrics.FetcherLabels{Static: t.static(), Result: metrics.Success}
-	if err := util.WriteFile(t.filename, raw, 0644); err != nil {
-		t.logger(ctx).Error("[discovery] Unable to write new topology to filesystem", "err", err)
-		metrics.Fetcher.File(l.WithResult(metrics.ErrWriteFile)).Inc()
-		return err
-	}
-	t.logger(ctx).Trace("[discovery] Topology written to filesystem", "file", t.filename,
-		"params", t.fetcher.Params)
-	metrics.Fetcher.File(l).Inc()
-	return nil
-}
-
-func (t *task) callHandler(ctx context.Context, topo *topology.RWTopology) (bool, error) {
-	updated, err := t.handler(topo)
-	if err != nil {
-		t.logger(ctx).Error("[discovery] Unable to handle topology", "err", err)
-	} else if updated {
-		t.logger(ctx).Trace("[discovery] Set topology", "params", t.fetcher.Params)
-	}
-	return updated, err
-}
-
-func (t *task) static() bool {
-	return t.mode == discovery.Static
-}
-
-func (t *task) logger(ctx context.Context) log.Logger {
-	return log.FromCtx(ctx).New("mode", t.mode)
-}
-
-type flag struct {
-	sync.Mutex
-	set bool
-	c   chan struct{}
-}
-
-func (f *flag) Set() {
-	f.Lock()
-	defer f.Unlock()
-	if !f.set {
-		close(f.c)
-	}
-	f.set = true
-}
diff --git a/go/lib/infra/modules/idiscovery/internal/metrics/BUILD.bazel b/go/lib/infra/modules/idiscovery/internal/metrics/BUILD.bazel
deleted file mode 100644
index 1508bd108..000000000
--- a/go/lib/infra/modules/idiscovery/internal/metrics/BUILD.bazel
+++ /dev/null
@@ -1,12 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-
-go_library(
-    name = "go_default_library",
-    srcs = ["metrics.go"],
-    importpath = "github.com/scionproto/scion/go/lib/infra/modules/idiscovery/internal/metrics",
-    visibility = ["//go/lib/infra/modules/idiscovery:__subpackages__"],
-    deps = [
-        "//go/lib/prom:go_default_library",
-        "@com_github_prometheus_client_golang//prometheus:go_default_library",
-    ],
-)
diff --git a/go/lib/infra/modules/idiscovery/internal/metrics/metrics.go b/go/lib/infra/modules/idiscovery/internal/metrics/metrics.go
deleted file mode 100644
index c5552e9d0..000000000
--- a/go/lib/infra/modules/idiscovery/internal/metrics/metrics.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2019 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package metrics
-
-import (
-	"github.com/prometheus/client_golang/prometheus"
-
-	"github.com/scionproto/scion/go/lib/prom"
-)
-
-// Namespace is the metrics namespace for the infra discovery module.
-const Namespace = "idiscovery"
-
-// Topology types.
-const (
-	Static  = "static"
-	Dynamic = "dynamic"
-)
-
-// Result labels.
-const (
-	Success      = prom.Success
-	OkIgnored    = "ok_ignored"
-	ErrRequest   = "err_request"
-	ErrUpdate    = "err_update"
-	ErrWriteFile = "err_write_file"
-)
-
-// Fetcher is the single-instance struct to get prometheus counters.
-var Fetcher = newFetcher()
-
-// FetcherLabels defines the requests label set.
-type FetcherLabels struct {
-	Static bool
-	Result string
-}
-
-// Labels returns the name of the labels in correct order.
-func (l FetcherLabels) Labels() []string {
-	return []string{"type", "result"}
-}
-
-// Values returns the values of the label in correct order.
-func (l FetcherLabels) Values() []string {
-	if l.Static {
-		return []string{Static, l.Result}
-	}
-	return []string{Dynamic, l.Result}
-}
-
-// WithResult returns the label set with the modified result.
-func (l FetcherLabels) WithResult(result string) FetcherLabels {
-	l.Result = result
-	return l
-}
-
-type fetcher struct {
-	sent, file *prometheus.CounterVec
-}
-
-func newFetcher() fetcher {
-	return fetcher{
-		sent: prom.NewCounterVecWithLabels(Namespace, "", "sent_requests_total",
-			"The total number of requests sent to the discovey service", FetcherLabels{}),
-		file: prom.NewCounterVecWithLabels(Namespace, "", "file_writes_total",
-			"The total number of file writes on updated topology", FetcherLabels{}),
-	}
-}
-
-// Sent returns the prometheus counter.
-func (r fetcher) Sent(l FetcherLabels) prometheus.Counter {
-	return r.sent.WithLabelValues(l.Values()...)
-}
-
-// File returns the prometheus counter.
-func (r fetcher) File(l FetcherLabels) prometheus.Counter {
-	return r.file.WithLabelValues(l.Values()...)
-}
diff --git a/go/lib/infra/modules/idiscovery/sample.go b/go/lib/infra/modules/idiscovery/sample.go
deleted file mode 100644
index 0666d5c75..000000000
--- a/go/lib/infra/modules/idiscovery/sample.go
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2019 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package idiscovery
-
-const staticSample = `
-# Enable periodic fetching of the static topology. (default false)
-Enable = false
-
-# Time between two consecutive static topology queries. (default 5m)
-Interval = "5m"
-
-# Timeout for querying the static topology. (default 1s)
-Timeout = "1s"
-
-# Require https connection. (default false)
-Https = false
-
-# Filename where the updated static topologies are written. In case of the
-# empty string, the updated topologies are not written. (default "")
-Filename = ""
-`
-
-const dynamicSample = `
-# Enable periodic fetching of the dynamic topology. (default false)
-Enable = false
-
-# Time between two consecutive dynamic topology queries. (default 5s)
-Interval = "5s"
-
-# Timeout for querying the dynamic topology. (default 1s)
-Timeout = "1s"
-
-# Require https connection. (default false)
-Https = false
-`
-
-const connectSample = `
-# Maximum time spent attempting to fetch the topology from the
-# discovery service on start. If no topology is successfully fetched
-# in this period, the FailAction is executed. (default 20s)
-InitialPeriod = "20s"
-
-# The action to take if no topology is successfully fetched in
-# the InitialPeriod.
-# - Fatal: Exit process.
-# - Continue: Log error and continue with execution.
-# (Fatal | Continue) (default Continue)
-FailAction = "Continue"
-`
diff --git a/go/lib/infra/modules/itopo/BUILD.bazel b/go/lib/infra/modules/itopo/BUILD.bazel
index f054c329d..4887867e1 100644
--- a/go/lib/infra/modules/itopo/BUILD.bazel
+++ b/go/lib/infra/modules/itopo/BUILD.bazel
@@ -3,7 +3,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
 go_library(
     name = "go_default_library",
     srcs = [
-        "cleaner.go",
         "doc.go",
         "itopo.go",
         "validate.go",
@@ -14,7 +13,6 @@ go_library(
         "//go/lib/common:go_default_library",
         "//go/lib/infra/modules/itopo/internal/metrics:go_default_library",
         "//go/lib/log:go_default_library",
-        "//go/lib/periodic:go_default_library",
         "//go/lib/serrors:go_default_library",
         "//go/lib/topology:go_default_library",
         "//go/proto:go_default_library",
diff --git a/go/lib/infra/modules/itopo/cleaner.go b/go/lib/infra/modules/itopo/cleaner.go
deleted file mode 100644
index 0af38d928..000000000
--- a/go/lib/infra/modules/itopo/cleaner.go
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2019 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package itopo
-
-import (
-	"context"
-	"fmt"
-	"time"
-
-	"github.com/scionproto/scion/go/lib/infra/modules/itopo/internal/metrics"
-	"github.com/scionproto/scion/go/lib/log"
-	"github.com/scionproto/scion/go/lib/periodic"
-)
-
-var _ periodic.Task = cleaner{}
-
-// StartCleaner starts a periodic task that removes expired dynamic topologies.
-func StartCleaner(period, timeout time.Duration, caller string) *periodic.Runner {
-	return periodic.Start(cleaner{caller}, period, timeout)
-}
-
-type cleaner struct {
-	caller string
-}
-
-// Name returns the tasks name.
-func (c cleaner) Name() string {
-	return fmt.Sprintf("%s_itopo_cleaner", c.caller)
-}
-
-// Run deletes expired dynamic topologies and calls the dropFunc passed to Init.
-func (c cleaner) Run(ctx context.Context) {
-	st.Lock()
-	defer st.Unlock()
-	if st.topo.dynamic != nil && !st.topo.dynamic.Active(time.Now()) {
-		log.FromCtx(ctx).Info("[itopo.cleaner] Dropping expired dynamic topology",
-			"ts", st.topo.dynamic.Timestamp, "ttl", st.topo.dynamic.TTL,
-			"expired", st.topo.dynamic.Expiry())
-		st.topo.dynamic = nil
-		call(st.clbks.CleanDynamic)
-		metrics.Current.Active().Set(0)
-	}
-}
diff --git a/go/lib/infra/modules/itopo/itopo.go b/go/lib/infra/modules/itopo/itopo.go
index 6f78c6944..c6403d52b 100644
--- a/go/lib/infra/modules/itopo/itopo.go
+++ b/go/lib/infra/modules/itopo/itopo.go
@@ -25,7 +25,6 @@ import (
 	"github.com/google/go-cmp/cmp"
 	"github.com/google/go-cmp/cmp/cmpopts"
 
-	"github.com/scionproto/scion/go/lib/common"
 	"github.com/scionproto/scion/go/lib/infra/modules/itopo/internal/metrics"
 	"github.com/scionproto/scion/go/lib/log"
 	"github.com/scionproto/scion/go/lib/serrors"
@@ -37,10 +36,6 @@ var st *state
 
 // Callbacks are callbacks to respond to specific topology update events.
 type Callbacks struct {
-	// CleanDynamic is called whenever dynamic topology is dropped due to expiration.
-	CleanDynamic func()
-	// DropDynamic is called whenever dynamic topology is dropped due to static update.
-	DropDynamic func()
 	// UpdateStatic is called whenever the pointer to static topology is updated.
 	UpdateStatic func()
 }
@@ -78,46 +73,15 @@ func Get() topology.Topology {
 	return topology.FromRWTopology(st.topo.Get())
 }
 
-// SetDynamic atomically sets the dynamic topology. The returned topology is a pointer
-// to the currently active topology at the end of the function call. It might differ from
-// the input topology. The second return value indicates whether the in-memory
-// copy of the dynamic topology has been updated.
-func SetDynamic(dynamic *topology.RWTopology) (*topology.RWTopology, bool, error) {
-	l := metrics.UpdateLabels{Type: metrics.Dynamic}
-	topo, updated, err := st.setDynamic(dynamic)
-	switch {
-	case err != nil:
-		l.Result = metrics.ErrValidate
-	case updated:
-		l.Result = metrics.Success
-	default:
-		l.Result = metrics.OkIgnored
-	}
-	incUpdateMetric(l)
-	return topo, updated, err
-}
-
-// BeginSetDynamic checks whether setting the dynamic topology is permissible. The returned
-// transaction provides a view on which topology would be active, if committed.
-func BeginSetDynamic(dynamic *topology.RWTopology) (Transaction, error) {
-	tx, err := st.beginSetDynamic(dynamic)
-	if err != nil {
-		incUpdateMetric(metrics.UpdateLabels{Type: metrics.Dynamic, Result: metrics.ErrValidate})
-	}
-	return tx, err
-}
-
 // SetStatic atomically sets the static topology. Whether semi-mutable fields are
 // allowed to change can be specified using semiMutAllowed. The returned
 // topology is a pointer to the currently active topology at the end of the function call.
 // It might differ from the input topology (same contents as existing static,
 // or dynamic set and still valid). The second return value indicates whether the in-memory
 // copy of the static topology has been updated.
-func SetStatic(static topology.Topology,
-	semiMutAllowed bool) (topology.Topology, bool, error) {
-
+func SetStatic(static topology.Topology) (topology.Topology, bool, error) {
 	l := metrics.UpdateLabels{Type: metrics.Static}
-	topo, updated, err := st.setStatic(static.Writable(), semiMutAllowed)
+	topo, updated, err := st.setStatic(static.Writable())
 	switch {
 	case err != nil:
 		l.Result = metrics.ErrValidate
@@ -132,8 +96,8 @@ func SetStatic(static topology.Topology,
 
 // BeginSetStatic checks whether setting the static topology is permissible. The returned
 // transaction provides a view on which topology would be active, if committed.
-func BeginSetStatic(static *topology.RWTopology, semiMutAllowed bool) (Transaction, error) {
-	tx, err := st.beginSetStatic(static, semiMutAllowed)
+func BeginSetStatic(static *topology.RWTopology) (Transaction, error) {
+	tx, err := st.beginSetStatic(static)
 	if err != nil {
 		incUpdateMetric(metrics.UpdateLabels{Type: metrics.Static, Result: metrics.ErrValidate})
 	}
@@ -177,8 +141,6 @@ func (tx *Transaction) Commit() error {
 		incUpdateMetric(l.WithResult(metrics.Success))
 		return nil
 	}
-	// Do transaction from dynamic topology update.
-	st.updateDynamic(tx.inputDynamic)
 	incUpdateMetric(l.WithResult(metrics.Success))
 	return nil
 }
@@ -227,74 +189,11 @@ func newState(id string, svc proto.ServiceType, clbks Callbacks) *state {
 	return s
 }
 
-// setDynamic atomically sets the dynamic topology.
-func (s *state) setDynamic(dynamic *topology.RWTopology) (*topology.RWTopology, bool, error) {
-	s.Lock()
-	defer s.Unlock()
-	if err := s.dynamicPreCheck(dynamic); err != nil {
-		return nil, false, err
-	}
-	if err := s.validator.Validate(dynamic, s.topo.static, false); err != nil {
-		return nil, false, err
-	}
-	if keepOld(dynamic, s.topo.dynamic) {
-		return s.topo.Get(), false, nil
-	}
-	s.updateDynamic(dynamic)
-	return s.topo.Get(), true, nil
-}
-
-func (s *state) updateDynamic(dynamic *topology.RWTopology) {
-	s.topo.dynamic = dynamic
-	cl := metrics.CurrentLabels{Type: metrics.Dynamic}
-	metrics.Current.Active().Set(1)
-	metrics.Current.Timestamp(cl).Set(metrics.Timestamp(dynamic.Timestamp))
-	metrics.Current.Expiry(cl).Set(metrics.Expiry(dynamic.Expiry()))
-}
-
-func (s *state) beginSetDynamic(dynamic *topology.RWTopology) (Transaction, error) {
-	s.Lock()
-	defer s.Unlock()
-	if err := s.dynamicPreCheck(dynamic); err != nil {
-		return Transaction{}, err
-	}
-	if err := s.validator.Validate(dynamic, s.topo.static, false); err != nil {
-		return Transaction{}, err
-	}
-	tx := Transaction{
-		candidateTopo:   s.topo,
-		staticAtTxStart: s.topo.static,
-		inputDynamic:    dynamic,
-	}
-	if !keepOld(tx.inputDynamic, tx.candidateTopo.dynamic) {
-		// The dynamic topology is only updated if it differs and is valid longer.
-		tx.candidateTopo.dynamic = dynamic
-	}
-	return tx, nil
-}
-
-func (s *state) dynamicPreCheck(dynamic *topology.RWTopology) error {
-	if dynamic == nil {
-		return serrors.New("Provided topology must not be nil")
-	}
-	if s.topo.static == nil {
-		return serrors.New("Static topology must be set")
-	}
-	now := time.Now()
-	if !dynamic.Active(now) {
-		return common.NewBasicError("Dynamic topology must be active", nil,
-			"ts", dynamic.Timestamp, "now", now, "expiry", dynamic.Expiry())
-	}
-	return nil
-}
-
 // setStatic atomically sets the static topology.
-func (s *state) setStatic(static *topology.RWTopology,
-	allowed bool) (*topology.RWTopology, bool, error) {
-
+func (s *state) setStatic(static *topology.RWTopology) (*topology.RWTopology, bool, error) {
 	s.Lock()
 	defer s.Unlock()
-	if err := s.validator.Validate(static, s.topo.static, allowed); err != nil {
+	if err := s.validator.Validate(static, s.topo.static); err != nil {
 		return nil, false, err
 	}
 	// Only update static topology if the new one is different or valid for longer.
@@ -305,10 +204,10 @@ func (s *state) setStatic(static *topology.RWTopology,
 	return s.topo.Get(), true, nil
 }
 
-func (s *state) beginSetStatic(static *topology.RWTopology, allowed bool) (Transaction, error) {
+func (s *state) beginSetStatic(static *topology.RWTopology) (Transaction, error) {
 	s.Lock()
 	defer s.Unlock()
-	if err := s.validator.Validate(static, s.topo.static, allowed); err != nil {
+	if err := s.validator.Validate(static, s.topo.static); err != nil {
 		return Transaction{}, err
 	}
 	tx := Transaction{
@@ -319,22 +218,12 @@ func (s *state) beginSetStatic(static *topology.RWTopology, allowed bool) (Trans
 	if keepOld(tx.inputStatic, tx.staticAtTxStart) {
 		return tx, nil
 	}
-	// Drop dynamic from candidate topo if it will be dropped when committing the transaction.
-	if s.validator.MustDropDynamic(tx.inputStatic, tx.staticAtTxStart) {
-		tx.candidateTopo.dynamic = nil
-	}
 	tx.candidateTopo.static = static
 	return tx, nil
 }
 
 // updateStatic updates the static topology, if necessary, and calls the corresponding callbacks.
 func (s *state) updateStatic(static *topology.RWTopology) {
-	// Drop dynamic topology if necessary.
-	if s.validator.MustDropDynamic(static, s.topo.static) && s.topo.dynamic != nil {
-		s.topo.dynamic = nil
-		call(s.clbks.DropDynamic)
-		metrics.Current.Active().Set(0)
-	}
 	s.topo.static = static
 	call(s.clbks.UpdateStatic)
 	cl := metrics.CurrentLabels{Type: metrics.Static}
diff --git a/go/lib/infra/modules/itopo/validate.go b/go/lib/infra/modules/itopo/validate.go
index f70493cc7..6caecfbfd 100644
--- a/go/lib/infra/modules/itopo/validate.go
+++ b/go/lib/infra/modules/itopo/validate.go
@@ -17,26 +17,16 @@ package itopo
 import (
 	"reflect"
 
-	"github.com/google/go-cmp/cmp"
-
 	"github.com/scionproto/scion/go/lib/common"
 	"github.com/scionproto/scion/go/lib/serrors"
 	"github.com/scionproto/scion/go/lib/topology"
 	"github.com/scionproto/scion/go/proto"
 )
 
-type dynDropper interface {
-	// MustDropDynamic indicates whether the dynamic topology shall be dropped.
-	MustDropDynamic(topo, oldTopo *topology.RWTopology) bool
-}
-
 // validator is used to validate that the topology update is permissible.
 type validator interface {
-	dynDropper
-	// Validate checks that the topology update is valid according to the mutability rules.
-	// The validation rules differ between service types. However, at the very least, it
-	// is checked that topo is not nil and the immutable parts do not change.
-	Validate(topo, oldTopo *topology.RWTopology, allowed bool) error
+	// Validate checks that the topology update is valid.
+	Validate(topo, oldTopo *topology.RWTopology) error
 }
 
 func validatorFactory(id string, svc proto.ServiceType) validator {
@@ -58,29 +48,23 @@ type validatorWrap struct {
 }
 
 // Validate checks that the topology update is valid.
-func (v *validatorWrap) Validate(topo, oldTopo *topology.RWTopology, allowed bool) error {
+func (v *validatorWrap) Validate(topo, oldTopo *topology.RWTopology) error {
 	if err := v.General(topo); err != nil {
 		return err
 	}
 	if err := v.Immutable(topo, oldTopo); err != nil {
 		return err
 	}
-	if err := v.SemiMutable(topo, oldTopo, allowed); err != nil {
-		return err
-	}
 	return nil
 }
 
 type internalValidator interface {
-	dynDropper
 	// General checks that the topology is generally valid. The exact check is implementation
 	// specific to the validator. However, at the very least, this check ensures that the
 	// provided topology is non-nil.
 	General(topo *topology.RWTopology) error
 	// Immutable checks that the immutable parts of the topology do not change.
 	Immutable(topo, oldTopo *topology.RWTopology) error
-	// SemiMutable checks that the semi-mutable parts of the topology update are valid.
-	SemiMutable(topo, oldTopo *topology.RWTopology, allowed bool) error
 }
 
 var _ internalValidator = (*generalValidator)(nil)
@@ -116,14 +100,6 @@ func (v *generalValidator) Immutable(topo, oldTopo *topology.RWTopology) error {
 	return nil
 }
 
-func (*generalValidator) SemiMutable(_, _ *topology.RWTopology, _ bool) error {
-	return nil
-}
-
-func (*generalValidator) MustDropDynamic(_, _ *topology.RWTopology) bool {
-	return false
-}
-
 var _ internalValidator = (*svcValidator)(nil)
 
 // svcValidator is used to validate updates if the element is a infra service.
@@ -201,46 +177,3 @@ func (v *brValidator) Immutable(topo, oldTopo *topology.RWTopology) error {
 	}
 	return nil
 }
-
-func (v *brValidator) SemiMutable(topo, oldTopo *topology.RWTopology, allowed bool) error {
-	if oldTopo == nil {
-		return nil
-	}
-	if !allowed && v.interfacesChanged(topo, oldTopo) {
-		return common.NewBasicError("IFID set changed", nil, "expected",
-			oldTopo.BR[v.id].IFIDs, "actual", topo.BR[v.id].IFIDs)
-	}
-	return nil
-}
-
-func (v *brValidator) MustDropDynamic(topo, oldTopo *topology.RWTopology) bool {
-	if oldTopo == nil {
-		return false
-	}
-	return v.interfacesChanged(topo, oldTopo)
-}
-
-func (v *brValidator) interfacesChanged(topo, oldTopo *topology.RWTopology) bool {
-	if v.interfaceSetChanged(topo, oldTopo) {
-		return true
-	}
-	for _, ifid := range topo.BR[v.id].IFIDs {
-		if !cmp.Equal(topo.IFInfoMap[ifid], oldTopo.IFInfoMap[ifid]) {
-			return true
-		}
-	}
-	return false
-}
-
-func (v *brValidator) interfaceSetChanged(topo, oldTopo *topology.RWTopology) bool {
-	if len(topo.BR[v.id].IFIDs) != len(oldTopo.BR[v.id].IFIDs) {
-		return true
-	}
-	// The interfaces are sorted.
-	for i, ifid := range topo.BR[v.id].IFIDs {
-		if ifid != oldTopo.BR[v.id].IFIDs[i] {
-			return true
-		}
-	}
-	return false
-}
diff --git a/go/lib/topology/interface.go b/go/lib/topology/interface.go
index 0ab39100b..2c8b2d32a 100644
--- a/go/lib/topology/interface.go
+++ b/go/lib/topology/interface.go
@@ -120,13 +120,6 @@ type Topology interface {
 	//
 	// XXX(scrye): Return value is a shallow copy.
 	Writable() *RWTopology
-
-	// DS returns the discovery servers in the topology.
-	//
-	// FIXME(scrye): Simplify return type and make it topology format agnostic.
-	//
-	// XXX(scrye): Return value is a shallow copy.
-	DS() IDAddrMap
 }
 
 // NewTopology creates a new empty topology.
@@ -159,10 +152,6 @@ type topologyS struct {
 	Topology *RWTopology
 }
 
-func (t *topologyS) DS() IDAddrMap {
-	return t.Topology.DS
-}
-
 func (t *topologyS) IA() addr.IA {
 	return t.Topology.IA
 }
diff --git a/go/lib/topology/json/json.go b/go/lib/topology/json/json.go
index 7576e70ab..0335e72b1 100644
--- a/go/lib/topology/json/json.go
+++ b/go/lib/topology/json/json.go
@@ -46,7 +46,6 @@ type Topology struct {
 	ZookeeperService map[int]*Address       `json:"ZookeeperService,omitempty"`
 	ControlService   map[string]*ServerInfo `json:"ControlService,omitempty"`
 	SIG              map[string]*ServerInfo `json:"SIG,omitempty"`
-	DiscoveryService map[string]*ServerInfo `json:"DiscoveryService,omitempty"`
 }
 
 // ServerInfo contains the information for a SCION application running in the local AS.
diff --git a/go/lib/topology/topology.go b/go/lib/topology/topology.go
index 3d301f1f7..bb7068fc4 100644
--- a/go/lib/topology/topology.go
+++ b/go/lib/topology/topology.go
@@ -68,7 +68,6 @@ type (
 		IFInfoMap IfInfoMap
 
 		CS  IDAddrMap
-		DS  IDAddrMap
 		SIG IDAddrMap
 	}
 
@@ -127,7 +126,6 @@ func NewRWTopology() *RWTopology {
 		BR:        make(map[string]BRInfo),
 		CS:        make(IDAddrMap),
 		SIG:       make(IDAddrMap),
-		DS:        make(IDAddrMap),
 		IFInfoMap: make(IfInfoMap),
 	}
 }
@@ -277,10 +275,6 @@ func (t *RWTopology) populateServices(raw *jsontopo.Topology) error {
 	if err != nil {
 		return serrors.WrapStr("unable to extract SIG address", err)
 	}
-	t.DS, err = svcMapFromRaw(raw.DiscoveryService)
-	if err != nil {
-		return serrors.WrapStr("unable to extract DS address", err)
-	}
 	return nil
 }
 
@@ -334,8 +328,6 @@ func (t *RWTopology) getSvcInfo(svc proto.ServiceType) (*svcInfo, error) {
 		return &svcInfo{idTopoAddrMap: t.CS}, nil
 	case proto.ServiceType_sig:
 		return &svcInfo{idTopoAddrMap: t.SIG}, nil
-	case proto.ServiceType_ds:
-		return &svcInfo{idTopoAddrMap: t.DS}, nil
 	default:
 		return nil, common.NewBasicError("Unsupported service type", nil, "type", svc)
 	}
diff --git a/go/sciond/BUILD.bazel b/go/sciond/BUILD.bazel
index da88a5710..4b3f106db 100644
--- a/go/sciond/BUILD.bazel
+++ b/go/sciond/BUILD.bazel
@@ -8,13 +8,11 @@ go_library(
     visibility = ["//visibility:private"],
     deps = [
         "//go/lib/common:go_default_library",
-        "//go/lib/discovery:go_default_library",
         "//go/lib/env:go_default_library",
         "//go/lib/fatal:go_default_library",
         "//go/lib/infra:go_default_library",
         "//go/lib/infra/infraenv:go_default_library",
         "//go/lib/infra/messenger/tcp:go_default_library",
-        "//go/lib/infra/modules/idiscovery:go_default_library",
         "//go/lib/infra/modules/itopo:go_default_library",
         "//go/lib/infra/modules/segfetcher:go_default_library",
         "//go/lib/infra/modules/trust:go_default_library",
diff --git a/go/sciond/internal/config/BUILD.bazel b/go/sciond/internal/config/BUILD.bazel
index 9061bee44..17421946b 100644
--- a/go/sciond/internal/config/BUILD.bazel
+++ b/go/sciond/internal/config/BUILD.bazel
@@ -11,7 +11,6 @@ go_library(
     deps = [
         "//go/lib/config:go_default_library",
         "//go/lib/env:go_default_library",
-        "//go/lib/infra/modules/idiscovery:go_default_library",
         "//go/lib/pathstorage:go_default_library",
         "//go/lib/sciond:go_default_library",
         "//go/lib/serrors:go_default_library",
@@ -26,7 +25,6 @@ go_test(
     embed = [":go_default_library"],
     deps = [
         "//go/lib/env/envtest:go_default_library",
-        "//go/lib/infra/modules/idiscovery/idiscoverytest:go_default_library",
         "//go/lib/pathstorage/pathstoragetest:go_default_library",
         "//go/lib/sciond:go_default_library",
         "//go/lib/truststorage/truststoragetest:go_default_library",
diff --git a/go/sciond/internal/config/config.go b/go/sciond/internal/config/config.go
index 959b5db29..6e80c2593 100644
--- a/go/sciond/internal/config/config.go
+++ b/go/sciond/internal/config/config.go
@@ -21,7 +21,6 @@ import (
 
 	"github.com/scionproto/scion/go/lib/config"
 	"github.com/scionproto/scion/go/lib/env"
-	"github.com/scionproto/scion/go/lib/infra/modules/idiscovery"
 	"github.com/scionproto/scion/go/lib/pathstorage"
 	"github.com/scionproto/scion/go/lib/sciond"
 	"github.com/scionproto/scion/go/lib/serrors"
@@ -36,14 +35,13 @@ var (
 var _ config.Config = (*Config)(nil)
 
 type Config struct {
-	General   env.General
-	Features  env.Features
-	Logging   env.Logging
-	Metrics   env.Metrics
-	Tracing   env.Tracing
-	TrustDB   truststorage.TrustDBConf
-	Discovery idiscovery.Config
-	SD        SDConfig
+	General  env.General
+	Features env.Features
+	Logging  env.Logging
+	Metrics  env.Metrics
+	Tracing  env.Tracing
+	TrustDB  truststorage.TrustDBConf
+	SD       SDConfig
 }
 
 func (cfg *Config) InitDefaults() {
@@ -54,7 +52,6 @@ func (cfg *Config) InitDefaults() {
 		&cfg.Metrics,
 		&cfg.Tracing,
 		&cfg.TrustDB,
-		&cfg.Discovery,
 		&cfg.SD,
 	)
 }
@@ -66,7 +63,6 @@ func (cfg *Config) Validate() error {
 		&cfg.Logging,
 		&cfg.Metrics,
 		&cfg.TrustDB,
-		&cfg.Discovery,
 		&cfg.SD,
 	)
 }
@@ -79,7 +75,6 @@ func (cfg *Config) Sample(dst io.Writer, path config.Path, _ config.CtxMap) {
 		&cfg.Metrics,
 		&cfg.Tracing,
 		&cfg.TrustDB,
-		&cfg.Discovery,
 		&cfg.SD,
 	)
 }
diff --git a/go/sciond/main.go b/go/sciond/main.go
index f4a3ff4f3..67e860e92 100644
--- a/go/sciond/main.go
+++ b/go/sciond/main.go
@@ -30,13 +30,11 @@ import (
 	"github.com/opentracing/opentracing-go"
 
 	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/discovery"
 	"github.com/scionproto/scion/go/lib/env"
 	"github.com/scionproto/scion/go/lib/fatal"
 	"github.com/scionproto/scion/go/lib/infra"
 	"github.com/scionproto/scion/go/lib/infra/infraenv"
 	"github.com/scionproto/scion/go/lib/infra/messenger/tcp"
-	"github.com/scionproto/scion/go/lib/infra/modules/idiscovery"
 	"github.com/scionproto/scion/go/lib/infra/modules/itopo"
 	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher"
 	"github.com/scionproto/scion/go/lib/infra/modules/trust"
@@ -60,8 +58,7 @@ const (
 )
 
 var (
-	cfg         config.Config
-	discRunners idiscovery.Runners
+	cfg config.Config
 )
 
 func init() {
@@ -90,10 +87,6 @@ func realMain() int {
 		log.Crit("Setup failed", "err", err)
 		return 1
 	}
-	if err := startDiscovery(cfg.Discovery); err != nil {
-		log.Crit("Unable to start topology fetcher", "err", err)
-		return 1
-	}
 	pathDB, revCache, err := pathstorage.NewPathStorage(cfg.SD.PathDB, cfg.SD.RevCache)
 	if err != nil {
 		log.Crit("Unable to initialize path storage", "err", err)
@@ -231,20 +224,13 @@ func setup() error {
 	if err != nil {
 		return common.NewBasicError("unable to load topology", err)
 	}
-	if _, _, err := itopo.SetStatic(topo, false); err != nil {
+	if _, _, err := itopo.SetStatic(topo); err != nil {
 		return common.NewBasicError("unable to set initial static topology", err)
 	}
 	infraenv.InitInfraEnvironment(cfg.General.Topology)
 	return nil
 }
 
-func startDiscovery(file idiscovery.Config) error {
-	var err error
-	discRunners, err = idiscovery.StartRunners(file, discovery.Default,
-		idiscovery.TopoHandlers{}, nil, "sd")
-	return err
-}
-
 func NewServer(network string, rsockPath string,
 	handlers servers.HandlerMap) (*servers.Server, func()) {
 
diff --git a/python/lib/topology.py b/python/lib/topology.py
index b5c2a1f64..b44461d6b 100644
--- a/python/lib/topology.py
+++ b/python/lib/topology.py
@@ -290,7 +290,6 @@ class Topology(object):
         type_map = {
             ServiceType.CS: self.control_servers,
             ServiceType.SIG: self.sigs,
-            ServiceType.DS: self.discovery_servers,
         }
         try:
             target = type_map[server_type]
diff --git a/python/lib/types.py b/python/lib/types.py
index 55cb4b46b..caafa777f 100644
--- a/python/lib/types.py
+++ b/python/lib/types.py
@@ -54,14 +54,8 @@ class ServiceType(TypeBase):
     # these values must be kept in sync with the common.capnp ServiceType enum
     #: Unset
     UNSET = "unset"
-    #: Beacon service
-    BS = "bs"
-    #: Path service
-    PS = "ps"
     #: Certificate service
     CS = "cs"
-    #: Discovery service
-    DS = "ds"
     #: Border router
     BR = "br"
     #: SCION-IP gateway
diff --git a/python/topology/generator.py b/python/topology/generator.py
index 6c1d36bfe..a954c92c5 100755
--- a/python/topology/generator.py
+++ b/python/topology/generator.py
@@ -45,8 +45,6 @@ def add_arguments(parser):
                         help='Enable TRACE level file logging in Go services')
     parser.add_argument('--pseg-ttl', type=int, default=DEFAULT_SEGMENT_TTL,
                         help='Path segment TTL (in seconds)')
-    parser.add_argument('-ds', '--discovery', action='store_true',
-                        help='Generate discovery service')
     parser.add_argument('-f', '--svcfrac', type=float, default=0.4,
                         help='Attempt SVC resolution in RPC calls for a fraction of\
                         available timeout')
diff --git a/python/topology/go.py b/python/topology/go.py
index aa2a02d24..e4e331b2b 100755
--- a/python/topology/go.py
+++ b/python/topology/go.py
@@ -93,7 +93,6 @@ class GoGenerator(object):
             'metrics': {
                 'Prometheus': prom_addr_br(name, v, DEFAULT_BR_PROM_PORT),
             },
-            'discovery': self._discovery_entry(),
             'br': {
                 'Profile': False,
             },
@@ -130,7 +129,6 @@ class GoGenerator(object):
                 'Backend': 'sqlite',
                 'Connection': os.path.join(self.db_dir, '%s.beacon.db' % name),
             },
-            'discovery': self._discovery_entry(),
             'tracing': self._tracing_entry(),
             'metrics': self._metrics_entry(name, infra_elem, CS_PROM_PORT),
             'quic': self._quic_conf_entry(CS_QUIC_PORT, self.args.svcfrac, infra_elem),
@@ -183,7 +181,6 @@ class GoGenerator(object):
                 'Backend': 'sqlite',
                 'Connection': os.path.join(self.db_dir, '%s.trust.db' % name),
             },
-            'discovery': self._discovery_entry(),
             'tracing': self._tracing_entry(),
             'metrics': self._metrics_entry(name, infra_elem, CO_PROM_PORT),
             'quic': self._quic_conf_entry(CO_QUIC_PORT, self.args.svcfrac, infra_elem),
@@ -266,7 +263,6 @@ class GoGenerator(object):
                 'Backend': 'sqlite',
                 'Connection': os.path.join(self.db_dir, '%s.trust.db' % name),
             },
-            'discovery': self._discovery_entry(),
             'sd': {
                 'address': socket_address_str(ip, SD_API_PORT),
                 'pathDB': {
@@ -315,17 +311,6 @@ class GoGenerator(object):
             },
         }
 
-    def _discovery_entry(self):
-        entry = {
-            'static': {
-                'Enable': self.args.discovery,
-            },
-            'dynamic': {
-                'Enable': self.args.discovery,
-            }
-        }
-        return entry
-
     def _tracing_entry(self):
         docker_ip = docker_host(self.args.in_docker, self.args.docker)
         entry = {
diff --git a/python/topology/topo.py b/python/topology/topo.py
index 1652e5b52..47aec568a 100755
--- a/python/topology/topo.py
+++ b/python/topology/topo.py
@@ -54,7 +54,6 @@ DEFAULT_BEACON_SERVERS = 1
 DEFAULT_GRACE_PERIOD = 18000
 DEFAULT_CONTROL_SERVERS = 1
 DEFAULT_COLIBRI_SERVERS = 1
-DEFAULT_DISCOVERY_SERVERS = 1
 
 UNDERLAY_4 = 'UDP/IPv4'
 UNDERLAY_6 = 'UDP/IPv6'
@@ -210,9 +209,6 @@ class TopoGenerator(object):
             srvs.append(("colibri_servers", DEFAULT_COLIBRI_SERVERS, "co", "ColibriService"))
         for conf_key, def_num, nick, topo_key in srvs:
             self._gen_srv_entry(topo_id, as_conf, conf_key, def_num, nick, topo_key)
-        # The discovery service does not run on top of the dispatcher.
-        self._gen_srv_entry(topo_id, as_conf, "discovery_servers", DEFAULT_DISCOVERY_SERVERS,
-                            "ds", "DiscoveryService", False)
 
     def _gen_srv_entry(self, topo_id, as_conf, conf_key, def_num, nick,
                        topo_key, uses_dispatcher=True):
@@ -242,8 +238,6 @@ class TopoGenerator(object):
     def _default_ctrl_port(self, nick):
         if nick == "cs":
             return 30252
-        if nick == "ds":
-            return 8041
         if nick == "co":
             return 30257
         print('Invalid nick: %s' % nick)
@@ -253,8 +247,6 @@ class TopoGenerator(object):
         count = as_conf.get(conf_key, def_num)
         if conf_key == "control_servers":
             count = 1
-        if conf_key == "discovery_servers" and not self.args.discovery:
-            count = 0
         return count
 
     def _gen_br_entries(self, topo_id, as_conf):


Test patch:
diff --git a/acceptance/discovery_br_fetches_dynamic_acceptance/test b/acceptance/discovery_br_fetches_dynamic_acceptance/test
deleted file mode 100755
index 27ed3e9ad..000000000
--- a/acceptance/discovery_br_fetches_dynamic_acceptance/test
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/bin/bash
-
-# This test checks that the border router fetches the dynamic topology
-# from the discovery service, and that an expired dynamic topology is dropped.
-#
-# The test is structured as follows:
-# 1. Check connectivity
-# 2. Serve dynamic topology with invalid BS address -> link goes down
-# 3. Check connectivity is broken
-# 4. Let dynamic topology expire -> link goes up
-# 5. Check connectivity is up again
-
-. acceptance/discovery_util/util.sh
-
-PROGRAM=`basename "$0"`
-COMMAND="$1"
-TEST_NAME="discovery_br_fetches_dynamic"
-
-DST_IA=${DST_IA:-1-ff00:0:112}
-DST_IA_FILE="$(ia_file $DST_IA)"
-
-
-test_setup() {
-    set -e
-    base_setup
-
-    local cfg="gen/ISD1/AS$AS_FILE/br$IA_FILE-1/br.toml"
-    echo $cfg
-    set_log_lvl "$cfg"
-    set_interval "$cfg" "dynamic"
-
-    base_start_scion
-    ./tools/dc start tester_$DST_IA_FILE tester_$IA_FILE
-}
-
-test_run() {
-    set -e
-    sleep 3
-    check_connectivity "initial check"
-
-    log "Staring to serve dynamic topology with wrong CS address."
-    jq ".ControlService[].Addrs.IPv4.Public.Addr = \"127.42.42.42\" | .Timestamp = $( date +%s) | .TTL = 7" $TOPO | sponge $DYNAMIC_FULL
-    log "Waiting for CS to issue revocation and link going down"
-    sleep 4
-    check_connectivity_broken "invalid beacon service address"
-
-    log "Waiting until dynamic topology expires and revocations are gone."
-    sleep 15
-    check_connectivity "dynamic topology expired"
-
-    log "Checking fail action."
-    check_br_fail_action "dynamic"
-}
-
-check_connectivity() {
-    bin/end2end_integration -src $IA -dst $DST_IA -attempts 5 -d || fail "FAIL: Traffic does not pass. step=( $1 )"
-}
-
-check_connectivity_broken() {
-    bin/end2end_integration -src $IA -dst $DST_IA -attempts 1 -d -log.console=crit || local failed=$?
-    if [ -z ${failed+x} ]; then
-        echo "FAIL: Traffic still passes. step=( $1 )"
-        return 1
-    fi
-}
-
-shift
-do_command $PROGRAM $COMMAND $TEST_NAME "$@"
diff --git a/acceptance/discovery_br_fetches_static_acceptance/test b/acceptance/discovery_br_fetches_static_acceptance/test
deleted file mode 100755
index 6f0e3ca05..000000000
--- a/acceptance/discovery_br_fetches_static_acceptance/test
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/bin/bash
-
-# This test checks that the border router fetches the static topology
-# from the discovery service, and that it writes the update to disk.
-#
-# The test is structured as follows:
-# 1. Check connectivity
-# 2. Serve static topology with invalid BS address -> link goes down
-# 3. Check connectivity is broken
-# 4. Check logs, and file diff
-# 4. Serve original static topology -> link goes up
-# 6. Check connectivity is up again
-
-. acceptance/discovery_util/util.sh
-
-PROGRAM=`basename "$0"`
-COMMAND="$1"
-TEST_NAME="discovery_br_fetches_static"
-
-DST_IA=${DST_IA:-1-ff00:0:112}
-DST_IA_FILE="$(ia_file $DST_IA)"
-
-test_setup() {
-    set -e
-    base_setup
-
-    for elem in gen/ISD1/AS$AS_FILE/br*; do
-        for cfg in $elem/*.toml; do
-            set_log_lvl "$cfg"
-            set_interval "$cfg" "static"
-            sed -i -e "/\[discovery.static]/a Filename = \"/share/logs/${elem##*/}-topo.json\"" $cfg
-        done
-    done
-
-    base_start_scion
-    ./tools/dc start tester_$DST_IA_FILE tester_$IA_FILE
-}
-
-test_run() {
-    set -e
-    sleep 3
-    check_connectivity "initial check"
-
-    log "Staring to serve static topology with wrong CS address."
-    jq ".ControlService[].Addrs.IPv4.Public.Addr = \"127.42.42.42\" | .Timestamp = $( date +%s)" $TOPO | sponge $STATIC_FULL
-    log "Waiting for CS to issue revocation and link going down"
-    sleep 4
-    check_connectivity_broken "invalid beacon service address"
-
-    log "Checking topology is set and written to file system."
-    check_logs "br$IA_FILE-1"
-    diff -q $STATIC_FULL logs/br$IA_FILE-1-topo.json
-
-    log "Setting original topology"
-    cp $TOPO $STATIC_FULL
-
-    log "Waiting for revocation to expire."
-    sleep 12
-    check_connectivity "serve original static topology"
-
-    log "Checking fail action."
-    check_br_fail_action "static"
-}
-
-check_connectivity_broken() {
-    bin/end2end_integration -src $IA -dst $DST_IA -attempts 1 -d -log.console=crit || local failed=$?
-    if [ -z ${failed+x} ]; then
-        echo "FAIL: Traffic still passes. step=( $1 )"
-        return 1
-    fi
-}
-
-check_connectivity() {
-    bin/end2end_integration -src $IA -dst $DST_IA -attempts 5 -d || fail "FAIL: Traffic does not pass. step=( $1 )"
-}
-
-check_logs() {
-    grep -q "\[discovery\] Set topology.*mode=static" "logs/$1.log" || \
-        fail "Setting static topology not found in logs. id=$1"
-    grep -q "\[discovery\] Topology written to filesystem.*mode=static" "logs/$1.log" || \
-        fail "Writing static topology not found in logs. id=$1"
-}
-
-shift
-do_command $PROGRAM $COMMAND $TEST_NAME "$@"
diff --git a/acceptance/discovery_infra_fetches_dynamic_acceptance/test b/acceptance/discovery_infra_fetches_dynamic_acceptance/test
deleted file mode 100755
index 8e9eb07ed..000000000
--- a/acceptance/discovery_infra_fetches_dynamic_acceptance/test
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/bin/bash
-
-# This test checks that the infra services fetch the dynamic topology
-# from the discovery service, and that an expired dynamic topology is dropped.
-
-PROGRAM=`basename "$0"`
-COMMAND="$1"
-TEST_NAME="discovery_infra_fetches_dynamic"
-
-. acceptance/discovery_util/util.sh
-
-test_setup() {
-    set -e
-    base_setup
-
-    for cfg in gen/ISD1/AS$AS_FILE/*/{cs,sd}.toml; do
-        set_log_lvl "$cfg"
-        set_interval "$cfg" "dynamic"
-    done
-
-    base_start_scion
-}
-
-test_run() {
-    set -e
-
-    log "Starting to serve dynamic topology with modified border router."
-    jq ".BorderRouters[].InternalAddrs.IPv4.PublicOverlay = {Addr: \"127.42.42.42\", OverlayPort: 39999} | .Timestamp = $( date +%s) | .TTL = 5" $TOPO | sponge $DYNAMIC_FULL
-    cp $DYNAMIC_FULL $DYNAMIC_DEFAULT
-    sleep 1
-    check_file "dynamic"
-
-    sleep 7
-    check_logs "cs$IA_FILE-1"
-    check_logs "sd$IA_FILE"
-
-    log "Checking fail action."
-    check_infra_fail_action "dynamic"
-}
-
-check_logs() {
-    log "Checking logs for $1."
-    grep -q "\[discovery\] Set topology.*mode=dynamic" "logs/$1.log" || \
-        fail "Setting dynamic topology not found in logs. id=$1"
-    grep -q "\[itopo.cleaner\] Dropping expired dynamic topology" "logs/$1.log" || \
-        fail "Dropping expired dynamic topology not found in logs. id=$1"
-}
-
-shift
-do_command $PROGRAM $COMMAND $TEST_NAME "$@"
diff --git a/acceptance/discovery_infra_fetches_static_acceptance/test b/acceptance/discovery_infra_fetches_static_acceptance/test
deleted file mode 100755
index 7984f400a..000000000
--- a/acceptance/discovery_infra_fetches_static_acceptance/test
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/bin/bash
-
-# This test checks that the infra services fetch the static topology
-# from the discovery service, and that it writes the update to disk.
-
-PROGRAM=`basename "$0"`
-COMMAND="$1"
-TEST_NAME="discovery_infra_fetches_static"
-
-. acceptance/discovery_util/util.sh
-
-test_setup() {
-    set -e
-    base_setup
-
-    for elem in gen/ISD1/AS$AS_FILE/{cs,endhost}*; do
-        for cfg in $elem/*.toml; do
-            set_log_lvl "$cfg"
-            set_interval "$cfg" "static"
-            elem=${elem##*/}
-            [ "$elem" != "endhost" ] || elem="sd$IA_FILE"
-            sed -i -e "/\[discovery.static]/a Filename = \"/share/cache/$elem-topo.json\"" $cfg
-        done
-    done
-
-    base_start_scion
-}
-
-test_run() {
-    set -e
-
-    log "Starting to serve static topology with modified border router."
-    jq ".BorderRouters[].InternalAddrs.IPv4.PublicOverlay = {Addr: \"127.42.42.42\", OverlayPort: 39999} | .Timestamp = $( date +%s)" $TOPO | sponge $STATIC_FULL
-    cp $STATIC_FULL $STATIC_DEFAULT
-    sleep 1
-    check_file "static"
-
-    sleep 3
-    check_logs "cs$IA_FILE-1"
-    check_logs "sd$IA_FILE"
-
-    check_diff "cs$IA_FILE-1"
-    check_diff "sd$IA_FILE"
-
-    log "Checking fail action."
-    check_infra_fail_action "static"
-}
-
-check_logs() {
-    log "Checking logs for $1."
-    grep -q "\[discovery\] Set topology.*mode=static" "logs/$1.log" || \
-        fail "Setting static topology not found in logs. id=$1"
-    grep -q "\[discovery\] Topology written to filesystem.*mode=static" "logs/$1.log" || \
-        fail "Writing static topology not found in logs. id=$1"
-}
-
-check_diff () {
-    log "Checking the stored static topology for $1."
-    diff -q $STATIC_FULL gen-cache/$1-topo.json
-}
-
-shift
-do_command $PROGRAM $COMMAND $TEST_NAME "$@"
diff --git a/go/border/brconf/params_test.go b/go/border/brconf/params_test.go
index 852f84f57..6ec6cd0b8 100644
--- a/go/border/brconf/params_test.go
+++ b/go/border/brconf/params_test.go
@@ -22,7 +22,6 @@ import (
 	"github.com/stretchr/testify/assert"
 
 	"github.com/scionproto/scion/go/lib/env/envtest"
-	"github.com/scionproto/scion/go/lib/infra/modules/idiscovery/idiscoverytest"
 )
 
 func TestConfigSample(t *testing.T) {
@@ -39,13 +38,8 @@ func TestConfigSample(t *testing.T) {
 
 func InitTestConfig(cfg *Config) {
 	envtest.InitTest(&cfg.General, &cfg.Logging, &cfg.Metrics, nil, nil)
-	InitTestDiscoveryConfig(&cfg.Discovery)
 	InitTestBRConfig(&cfg.BR)
 }
-func InitTestDiscoveryConfig(cfg *Discovery) {
-	cfg.AllowSemiMutable = true
-	idiscoverytest.InitTestConfig(&cfg.Config)
-}
 
 func InitTestBRConfig(cfg *BR) {
 	cfg.Profile = true
@@ -53,15 +47,9 @@ func InitTestBRConfig(cfg *BR) {
 
 func CheckTestConfig(t *testing.T, cfg *Config, id string) {
 	envtest.CheckTest(t, &cfg.General, &cfg.Logging, &cfg.Metrics, nil, nil, id)
-	CheckTestDiscoveryConfig(t, &cfg.Discovery)
 	CheckTestBRConfig(t, &cfg.BR)
 }
 
-func CheckTestDiscoveryConfig(t *testing.T, cfg *Discovery) {
-	assert.False(t, cfg.AllowSemiMutable)
-	idiscoverytest.CheckTestConfig(t, &cfg.Config)
-}
-
 func CheckTestBRConfig(t *testing.T, cfg *BR) {
 	assert.False(t, cfg.Profile)
 	assert.Equal(t, FailActionFatal, cfg.RollbackFailAction)
diff --git a/go/cs/config/config_test.go b/go/cs/config/config_test.go
index 5394dfb2f..de11d4ae4 100644
--- a/go/cs/config/config_test.go
+++ b/go/cs/config/config_test.go
@@ -25,7 +25,6 @@ import (
 	"github.com/scionproto/scion/go/cs/beaconstorage/beaconstoragetest"
 	"github.com/scionproto/scion/go/lib/ctrl/path_mgmt"
 	"github.com/scionproto/scion/go/lib/env/envtest"
-	"github.com/scionproto/scion/go/lib/infra/modules/idiscovery/idiscoverytest"
 	"github.com/scionproto/scion/go/lib/pathstorage/pathstoragetest"
 	"github.com/scionproto/scion/go/lib/truststorage/truststoragetest"
 	"github.com/scionproto/scion/go/lib/util"
@@ -61,7 +60,6 @@ func InitTestConfig(cfg *Config) {
 	envtest.InitTest(&cfg.General, &cfg.Logging, &cfg.Metrics, &cfg.Tracing, nil)
 	truststoragetest.InitTestConfig(&cfg.TrustDB)
 	beaconstoragetest.InitTestBeaconDBConf(&cfg.BeaconDB)
-	idiscoverytest.InitTestConfig(&cfg.Discovery)
 	InitTestBSConfig(&cfg.BS)
 }
 
@@ -80,7 +78,6 @@ func CheckTestConfig(t *testing.T, cfg *Config, id string) {
 	envtest.CheckTest(t, &cfg.General, &cfg.Logging, &cfg.Metrics, &cfg.Tracing, nil, id)
 	truststoragetest.CheckTestConfig(t, &cfg.TrustDB, id)
 	beaconstoragetest.CheckTestBeaconDBConf(t, &cfg.BeaconDB, id)
-	idiscoverytest.CheckTestConfig(t, &cfg.Discovery)
 	CheckTestBSConfig(t, &cfg.BS)
 	CheckTestCSConfig(t, &cfg.CS)
 	CheckTestPSConfig(t, &cfg.PS, id)
diff --git a/go/lib/discovery/discoveryinfo/info_test.go b/go/lib/discovery/discoveryinfo/info_test.go
deleted file mode 100644
index 0a6a0b13d..000000000
--- a/go/lib/discovery/discoveryinfo/info_test.go
+++ /dev/null
@@ -1,96 +0,0 @@
-package discoveryinfo
-
-import (
-	"net"
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func TestNew(t *testing.T) {
-	cases := map[string]struct {
-		key  string
-		addr *net.UDPAddr
-	}{
-		"empty address": {key: "x", addr: &net.UDPAddr{}},
-	}
-
-	for n, tc := range cases {
-		t.Run(n, func(t *testing.T) {
-			got := New(tc.key, tc.addr)
-			assert.NotNil(t, got)
-		})
-	}
-}
-
-func TestUpdate(t *testing.T) {
-	cases := map[string]struct {
-		before, after *Info
-		input         *net.UDPAddr
-	}{
-		"nil addr": {
-			before: &Info{
-				addr:      &net.UDPAddr{},
-				failCount: 10,
-			},
-			input: &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234},
-			after: &Info{
-				addr:      &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234},
-				failCount: 0,
-			},
-		},
-		"not nil addr": {
-			before: &Info{
-				addr:      &net.UDPAddr{IP: net.ParseIP("127.0.0.10"), Port: 1234},
-				failCount: 10,
-			},
-			input: &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234},
-			after: &Info{
-				addr:      &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234},
-				failCount: 0,
-			},
-		},
-		"same addr": {
-			before: &Info{
-				addr:      &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234},
-				failCount: 10,
-			},
-			input: &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234},
-			after: &Info{
-				addr:      &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234},
-				failCount: 10,
-			},
-		},
-	}
-
-	for n, tc := range cases {
-		t.Run(n, func(t *testing.T) {
-			tc.before.Update(tc.input)
-			tc.input.IP[0] = 42 // other part of the code modifies the input reference.
-			assert.Equal(t, tc.before.addr, tc.after.addr)
-			assert.Equal(t, tc.before.failCount, tc.after.failCount)
-		})
-	}
-}
-
-func TestAddr(t *testing.T) {
-	cases := map[string]struct {
-		before *Info
-		want   *net.UDPAddr
-	}{
-		"addr": {
-			before: &Info{
-				addr: &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234},
-			},
-			want: &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234},
-		},
-	}
-
-	for n, tc := range cases {
-		t.Run(n, func(t *testing.T) {
-			got := tc.before.Addr()
-			tc.before.addr.IP[0] = 42 // other part of the code modifies the IP reference.
-			assert.Equal(t, got, tc.want)
-		})
-	}
-}
diff --git a/go/lib/discovery/discoverypool/pool_test.go b/go/lib/discovery/discoverypool/pool_test.go
deleted file mode 100644
index d5b1c02b1..000000000
--- a/go/lib/discovery/discoverypool/pool_test.go
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2018 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package discoverypool
-
-import (
-	"net"
-	"testing"
-
-	. "github.com/smartystreets/goconvey/convey"
-
-	"github.com/scionproto/scion/go/lib/topology"
-	"github.com/scionproto/scion/go/lib/xtest"
-)
-
-type testInfo struct {
-	key  string
-	addr *net.UDPAddr
-}
-
-var ds = []testInfo{
-	{"ds1-ff00_0_111-1", &net.UDPAddr{
-		IP:   net.IPv4(127, 0, 0, 22),
-		Port: 30084,
-	},
-	},
-	{"ds1-ff00_0_111-2", &net.UDPAddr{
-		IP:   net.IPv4(127, 0, 0, 80),
-		Port: 30085,
-	},
-	},
-}
-
-func contains(pool *Pool, v testInfo) {
-	Convey("The pool contains "+v.key, func() {
-		info, ok := pool.m[v.key]
-		SoMsg("Not found", ok, ShouldBeTrue)
-		SoMsg("Ip", info.Addr().IP.Equal(v.addr.IP), ShouldBeTrue)
-		SoMsg("Port", info.Addr().Port, ShouldEqual, v.addr.Port)
-	})
-}
-
-func TestNew(t *testing.T) {
-	Convey("Given a topology", t, func() {
-		svcInfo := mustLoadSvcInfo(t)
-		Convey("When the topology contains a discovery service", func() {
-			pool, err := New(svcInfo)
-			Convey("The pool should initialize", func() {
-				So(err, ShouldBeNil)
-			})
-			Convey("The pool should contain all discovery services", func() {
-				for _, v := range ds {
-					contains(pool, v)
-				}
-			})
-		})
-		Convey("When the topology does not contain a discovery service", func() {
-			Convey("The pool should not initialize", func() {
-				_, err := New(nil)
-				So(err, ShouldNotBeNil)
-			})
-		})
-	})
-}
-
-func TestPoolUpdate(t *testing.T) {
-	Convey("Given a pool", t, func() {
-		pool := mustLoadPool(t)
-		svcInfo := mustLoadSvcInfo(t)
-		Convey("And a topology containing an updated discovery service entry", func() {
-			svcInfo[ds[0].key].SCIONAddress.IP = net.IPv4(127, 0, 0, 21)
-			pool.Update(svcInfo)
-			Convey("The pool should contain the updated info", func() {
-				contains(pool, testInfo{
-					key: ds[0].key,
-					addr: &net.UDPAddr{
-						IP:   net.IPv4(127, 0, 0, 21),
-						Port: svcInfo[ds[0].key].SCIONAddress.Port,
-					},
-				})
-			})
-		})
-		Convey("And a topology containing new discovery service entries", func() {
-			svcInfo["ds-new"] = svcInfo[ds[0].key]
-			pool.Update(svcInfo)
-			Convey("The pool should contain all discovery services", func() {
-				for _, v := range ds {
-					contains(pool, v)
-				}
-				contains(pool, testInfo{
-					key: "ds-new",
-					addr: &net.UDPAddr{
-						IP:   net.IPv4(127, 0, 0, 22),
-						Port: 30084},
-				})
-			})
-		})
-		Convey("And a topology with some removed discovery service", func() {
-			delete(svcInfo, ds[1].key)
-			pool.Update(svcInfo)
-			Convey("The pool should contain all remaining discovery services", func() {
-				for _, v := range ds[:1] {
-					contains(pool, v)
-				}
-			})
-			Convey("The pool should not contain the removed discovery service", func() {
-				_, ok := pool.m[ds[1].key]
-				So(ok, ShouldBeFalse)
-			})
-		})
-		Convey("And a topology with no discovery service", func() {
-			pool.Update(nil)
-			Convey("The pool should still contain all services", func() {
-				for _, v := range ds[:1] {
-					contains(pool, v)
-				}
-			})
-		})
-	})
-}
-
-func mustLoadPool(t *testing.T) *Pool {
-	pool, err := New(mustLoadSvcInfo(t))
-	xtest.FailOnErr(t, err)
-	return pool
-}
-
-func mustLoadSvcInfo(t *testing.T) topology.IDAddrMap {
-	topo, err := topology.FromJSONFile("testdata/topology.json")
-	xtest.FailOnErr(t, err)
-	return topo.DS()
-}
diff --git a/go/lib/discovery/discoverypool/testdata/topology.json b/go/lib/discovery/discoverypool/testdata/topology.json
deleted file mode 100644
index a66e5dd09..000000000
--- a/go/lib/discovery/discoverypool/testdata/topology.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-  "DiscoveryService": {
-    "ds1-ff00_0_111-1": {
-      "Addrs": {
-        "IPv4": {
-          "Public": {
-            "Addr": "127.0.0.22",
-            "L4Port": 30084
-          }
-        }
-      }
-    },
-    "ds1-ff00_0_111-2": {
-      "Addrs": {
-        "IPv4": {
-          "Public": {
-            "Addr": "127.0.0.80",
-            "L4Port": 30085
-          }
-        }
-      }
-    }
-  },
-  "Overlay": "UDP/IPv4",
-  "ISD_AS": "1-ff00:0:111"
-}
diff --git a/go/lib/healthpool/info_test.go b/go/lib/healthpool/info_test.go
deleted file mode 100644
index f8e9a1cd1..000000000
--- a/go/lib/healthpool/info_test.go
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2019 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package healthpool
-
-import (
-	"testing"
-	"time"
-
-	. "github.com/smartystreets/goconvey/convey"
-)
-
-func TestFail(t *testing.T) {
-	Convey("The fail count should increase correctly", t, func() {
-		info := info{}
-		Convey("Increment by one when smaller than MaxFailCount", func() {
-			SoMsg("Initial FailCount", info.FailCount(), ShouldEqual, 0)
-			info.Fail()
-			SoMsg("FailCount", info.FailCount(), ShouldEqual, 1)
-		})
-		Convey("Stay the same when equal than MaxFailCount", func() {
-			info.fails = uint16(MaxFailCount)
-			info.Fail()
-			SoMsg("FailCount", info.FailCount(), ShouldEqual, MaxFailCount)
-		})
-	})
-}
-
-func TestExpireFails(t *testing.T) {
-	Convey("The fail count should expire correctly", t, func() {
-		initFails := uint16(64)
-		now := time.Now()
-		t := []struct {
-			desc     string
-			lastFail time.Time
-			lastExp  time.Time
-			opts     ExpireOptions
-			expFails uint16
-		}{
-			{
-				desc:     "No expiration when time since lastFail is less than start",
-				lastFail: now,
-				expFails: initFails,
-			},
-			{
-				desc:     "Expiration when time since lastFail is equal to start",
-				lastFail: now.Add(-DefaultExpireStart),
-				expFails: initFails >> 1,
-			},
-			{
-				desc:     "Expiration when time since lastFail is equal to start + interval",
-				lastFail: now.Add(-(DefaultExpireStart + DefaultExpireInterval)),
-				expFails: initFails >> 2,
-			},
-			{
-				desc:     "Expiration when time since lastFail is equal to start + 2 intervals",
-				lastFail: now.Add(-(DefaultExpireStart + 2*DefaultExpireInterval)),
-				expFails: initFails >> 3,
-			},
-			{
-				desc:     "No expiration when time since lastExp less than interval",
-				lastFail: now.Add(-(DefaultExpireStart + 5*DefaultExpireInterval)),
-				lastExp:  now.Add(-DefaultExpireInterval + time.Second),
-				expFails: initFails,
-			},
-			{
-				desc:     "Expiration when time since lastExp is equal to interval",
-				lastFail: now.Add(-(DefaultExpireStart + 5*DefaultExpireInterval)),
-				lastExp:  now.Add(-DefaultExpireInterval),
-				expFails: initFails >> 1,
-			},
-			{
-				desc:     "Expiration when time since lastExp is equal to 2 intervals",
-				lastFail: now.Add(-(DefaultExpireStart + 5*DefaultExpireInterval)),
-				lastExp:  now.Add(-2 * DefaultExpireInterval),
-				expFails: initFails >> 2,
-			},
-		}
-		for _, v := range t {
-			Convey(v.desc, func() {
-				inf := &info{
-					lastFail: v.lastFail,
-					lastExp:  v.lastExp,
-					fails:    initFails,
-				}
-				inf.expireFails(now, ExpireOptions{})
-				SoMsg("FailCount", inf.FailCount(), ShouldEqual, v.expFails)
-			})
-		}
-	})
-}
diff --git a/go/lib/healthpool/pool_test.go b/go/lib/healthpool/pool_test.go
deleted file mode 100644
index 3f8b92ba7..000000000
--- a/go/lib/healthpool/pool_test.go
+++ /dev/null
@@ -1,185 +0,0 @@
-// Copyright 2018 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package healthpool
-
-import (
-	"testing"
-	"time"
-
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-)
-
-type testInfo struct {
-	Info
-	name string
-}
-
-func TestNewPool(t *testing.T) {
-	_, _, infos := testInfoSet()
-	tests := map[string]struct {
-		Infos     InfoSet
-		Options   PoolOptions
-		Assertion require.ErrorAssertionFunc
-	}{
-		"valid": {
-			Infos:     infos,
-			Assertion: require.NoError,
-		},
-		"allow empty": {
-			Options:   PoolOptions{AllowEmpty: true},
-			Assertion: require.NoError,
-		},
-		"empty": {
-			Assertion: require.Error,
-		},
-		"invalid algorithm": {
-			Options:   PoolOptions{Algorithm: "invalid", AllowEmpty: true},
-			Assertion: require.Error,
-		},
-	}
-
-	for name, test := range tests {
-		name, test := name, test
-		t.Run(name, func(t *testing.T) {
-			t.Parallel()
-			p, err := NewPool(test.Infos, test.Options)
-			test.Assertion(t, err)
-			if err == nil {
-				assert.ElementsMatch(t, test.Infos.List(), p.Infos())
-			}
-		})
-	}
-}
-
-func TestPoolUpdate(t *testing.T) {
-	_, two, infos := testInfoSet()
-	p, err := NewPool(infos, PoolOptions{})
-	require.NoError(t, err)
-
-	// Added entry should be part of the pool.
-	infos[newTestInfo("three")] = struct{}{}
-	err = p.Update(infos)
-	require.NoError(t, err)
-	assert.ElementsMatch(t, infos.List(), p.Infos())
-
-	// Removed entry should no longer be part of the pool.
-	delete(infos, two)
-	err = p.Update(infos)
-	require.NoError(t, err)
-	assert.ElementsMatch(t, infos.List(), p.Infos())
-	assert.NotContains(t, p.Infos(), two)
-
-	// Empty update only succeeds when allow empty is set.
-	err = p.Update(nil)
-	require.Error(t, err)
-	p.opts.AllowEmpty = true
-	err = p.Update(nil)
-	require.NoError(t, err)
-}
-
-func TestPoolChoose(t *testing.T) {
-	one, two, infos := testInfoSet()
-	p, err := NewPool(infos, PoolOptions{})
-	require.NoError(t, err)
-
-	one.Fail()
-	i, err := p.Choose()
-	require.NoError(t, err)
-	assert.Equal(t, two, i)
-
-	two.Fail()
-	two.Fail()
-	i, err = p.Choose()
-	require.NoError(t, err)
-	assert.Equal(t, one, i)
-
-	two.ResetCount()
-	i, err = p.Choose()
-	require.NoError(t, err)
-	assert.Equal(t, two, i)
-
-	one.(*testInfo).Info.(*info).fails = uint16(MaxFailCount)
-	two.(*testInfo).Info.(*info).fails = uint16(MaxFailCount)
-	_, err = p.Choose()
-	assert.NoError(t, err)
-}
-
-func TestPoolChooseEmpty(t *testing.T) {
-	p, err := NewPool(nil, PoolOptions{AllowEmpty: true})
-	require.NoError(t, err)
-	_, err = p.Choose()
-	assert.Error(t, err)
-}
-
-func TestPoolClose(t *testing.T) {
-	_, _, infos := testInfoSet()
-	p, err := NewPool(infos, PoolOptions{})
-	require.NoError(t, err)
-	p.Close()
-	_, err = p.Choose()
-	assert.Error(t, err)
-	assert.Error(t, p.Update(infos))
-	assert.NotPanics(t, p.Close)
-}
-
-func TestPoolExpiresFails(t *testing.T) {
-	initTime := time.Now().Add(-(time.Hour + 10*time.Second))
-	one := &info{
-		lastExp:  initTime,
-		lastFail: initTime,
-		fails:    64,
-	}
-	two := &info{
-		lastExp:  initTime,
-		lastFail: initTime,
-		fails:    128,
-	}
-	infos := InfoSet{
-		one: {},
-		two: {},
-	}
-	p, err := NewPool(
-		infos,
-		PoolOptions{
-			Expire: ExpireOptions{
-				Interval: time.Hour / 2,
-				Start:    time.Nanosecond,
-			},
-		},
-	)
-	require.NoError(t, err)
-	p.expirer.TriggerRun()
-	time.Sleep(time.Millisecond)
-	assert.Equal(t, 16, one.FailCount())
-	assert.Equal(t, 32, two.FailCount())
-}
-
-func testInfoSet() (Info, Info, InfoSet) {
-	one := newTestInfo("one")
-	two := newTestInfo("two")
-	infos := InfoSet{
-		one: {},
-		two: {},
-	}
-	return one, two, infos
-}
-
-func newTestInfo(name string) *testInfo {
-	return &testInfo{
-		Info: NewInfo(),
-		name: name,
-	}
-}
diff --git a/go/lib/healthpool/svcinstance/pool_test.go b/go/lib/healthpool/svcinstance/pool_test.go
deleted file mode 100644
index c6f64f904..000000000
--- a/go/lib/healthpool/svcinstance/pool_test.go
+++ /dev/null
@@ -1,167 +0,0 @@
-// Copyright 2019 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package svcinstance
-
-import (
-	"net"
-	"testing"
-
-	. "github.com/smartystreets/goconvey/convey"
-
-	"github.com/scionproto/scion/go/lib/healthpool"
-	"github.com/scionproto/scion/go/lib/topology"
-	"github.com/scionproto/scion/go/lib/xtest"
-)
-
-const (
-	ds1        = "ds1-ff00_0_111-1"
-	ds1updated = "ds1-ff00_0_111-1-updated"
-	ds2        = "ds1-ff00_0_111-2"
-	ds3new     = "ds1-ff00_0_111-3-new"
-)
-
-var dsInfos = map[string]*net.UDPAddr{
-	ds1: {
-		IP:   net.IPv4(127, 0, 0, 22),
-		Port: 30084,
-	},
-	ds2: {
-		IP:   net.IPv4(127, 0, 0, 80),
-		Port: 30085,
-	},
-	ds3new: {
-		IP:   net.IPv4(127, 0, 0, 22),
-		Port: 30084,
-	},
-	ds1updated: {
-		IP:   net.IPv4(127, 0, 0, 21),
-		Port: 30084,
-	},
-}
-
-func TestNewPool(t *testing.T) {
-	Convey("Given a non-empty service instance map", t, func() {
-		pool, err := NewPool(mustLoadSvcInfo(t), healthpool.PoolOptions{})
-		SoMsg("err", err, ShouldBeNil)
-		Convey("The pool should contain all discovery services", func() {
-			containsAll(pool, ds1, ds2)
-		})
-	})
-	Convey("Given an empty instance map, initialize only when AllowEmpty is set", t, func() {
-		_, err := NewPool(nil, healthpool.PoolOptions{})
-		SoMsg("!AllowEmpty", err, ShouldNotBeNil)
-		_, err = NewPool(nil, healthpool.PoolOptions{AllowEmpty: true})
-		SoMsg("AllowEmpty", err, ShouldBeNil)
-	})
-}
-
-func TestPoolUpdate(t *testing.T) {
-	Convey("Given a pool", t, func() {
-		pool := mustLoadPool(t)
-		svcInfo := mustLoadSvcInfo(t)
-		Convey("And an instance map containing an updated discovery service entry", func() {
-			svcInfo[ds1].SCIONAddress.IP = dsInfos[ds1updated].IP
-			pool.infos[ds1].Fail()
-			err := pool.Update(svcInfo)
-			SoMsg("err", err, ShouldBeNil)
-			Convey("The pool should contain the updated and reset info", func() {
-				contains(pool, ds1, dsInfos[ds1updated])
-				SoMsg("FailCount", pool.infos[ds1].FailCount(), ShouldBeZeroValue)
-			})
-		})
-		Convey("And an instance map containing new discovery service entries", func() {
-			svcInfo[ds3new] = svcInfo[ds1]
-			err := pool.Update(svcInfo)
-			SoMsg("err", err, ShouldBeNil)
-			Convey("The pool should contain all discovery services", func() {
-				containsAll(pool, ds1, ds2, ds3new)
-			})
-		})
-		Convey("And an instance map with some removed discovery service", func() {
-			delete(svcInfo, ds2)
-			err := pool.Update(svcInfo)
-			SoMsg("err", err, ShouldBeNil)
-			Convey("The pool should contain all remaining discovery services", func() {
-				containsAll(pool, ds1)
-			})
-			Convey("The pool should not contain the removed discovery service", func() {
-				_, ok := pool.infos[ds2]
-				So(ok, ShouldBeFalse)
-			})
-		})
-		Convey("And an instance map with no discovery service", func() {
-			err := pool.Update(nil)
-			SoMsg("err", err, ShouldNotBeNil)
-			Convey("The pool should still contain all services", func() {
-				containsAll(pool, ds1, ds2)
-			})
-		})
-	})
-}
-
-func TestPoolChoose(t *testing.T) {
-	Convey("Given an initialized pool", t, func() {
-		p := mustLoadPool(t)
-		p.infos[ds1].Fail()
-		i, err := p.Choose()
-		SoMsg("err first", err, ShouldBeNil)
-		SoMsg("Choose first", i.Addr().IP.Equal(p.infos[ds2].addr.IP), ShouldBeTrue)
-		SoMsg("Name first", i.Name(), ShouldEqual, ds2)
-		i.Fail()
-		i.Fail()
-		i, err = p.Choose()
-		SoMsg("err second", err, ShouldBeNil)
-		SoMsg("Choose second", i.Addr().IP.Equal(p.infos[ds1].addr.IP), ShouldBeTrue)
-		SoMsg("Name second", i.Name(), ShouldEqual, ds1)
-	})
-}
-
-func TestPoolClose(t *testing.T) {
-	Convey("Given a closed pool", t, func() {
-		p := mustLoadPool(t)
-		p.Close()
-		_, err := p.Choose()
-		SoMsg("Choose should fail", err, ShouldNotBeNil)
-		SoMsg("Update should fail", p.Update(mustLoadSvcInfo(t)), ShouldNotBeNil)
-		SoMsg("Close should not panic", p.Close, ShouldNotPanic)
-	})
-}
-
-func containsAll(p *Pool, names ...string) {
-	for _, name := range names {
-		contains(p, name, dsInfos[name])
-	}
-}
-
-func contains(p *Pool, name string, a *net.UDPAddr) {
-	Convey("The pool contains "+name, func() {
-		info, ok := p.infos[name]
-		SoMsg("Not found", ok, ShouldBeTrue)
-		SoMsg("Ip", info.addr.IP.String(), ShouldResemble, a.IP.String())
-		SoMsg("Port", info.addr.Port, ShouldEqual, a.Port)
-	})
-}
-
-func mustLoadPool(t *testing.T) *Pool {
-	pool, err := NewPool(mustLoadSvcInfo(t), healthpool.PoolOptions{})
-	xtest.FailOnErr(t, err)
-	return pool
-}
-
-func mustLoadSvcInfo(t *testing.T) topology.IDAddrMap {
-	topo, err := topology.FromJSONFile("testdata/topology.json")
-	xtest.FailOnErr(t, err)
-	return topo.DS()
-}
diff --git a/go/lib/healthpool/svcinstance/testdata/topology.json b/go/lib/healthpool/svcinstance/testdata/topology.json
deleted file mode 100644
index a66e5dd09..000000000
--- a/go/lib/healthpool/svcinstance/testdata/topology.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-  "DiscoveryService": {
-    "ds1-ff00_0_111-1": {
-      "Addrs": {
-        "IPv4": {
-          "Public": {
-            "Addr": "127.0.0.22",
-            "L4Port": 30084
-          }
-        }
-      }
-    },
-    "ds1-ff00_0_111-2": {
-      "Addrs": {
-        "IPv4": {
-          "Public": {
-            "Addr": "127.0.0.80",
-            "L4Port": 30085
-          }
-        }
-      }
-    }
-  },
-  "Overlay": "UDP/IPv4",
-  "ISD_AS": "1-ff00:0:111"
-}
diff --git a/go/lib/infra/modules/idiscovery/idiscoverytest/BUILD.bazel b/go/lib/infra/modules/idiscovery/idiscoverytest/BUILD.bazel
deleted file mode 100644
index 50ff9c33d..000000000
--- a/go/lib/infra/modules/idiscovery/idiscoverytest/BUILD.bazel
+++ /dev/null
@@ -1,23 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
-
-go_library(
-    name = "go_default_library",
-    srcs = ["config.go"],
-    importpath = "github.com/scionproto/scion/go/lib/infra/modules/idiscovery/idiscoverytest",
-    visibility = ["//visibility:public"],
-    deps = [
-        "//go/lib/infra/modules/idiscovery:go_default_library",
-        "@com_github_stretchr_testify//assert:go_default_library",
-    ],
-)
-
-go_test(
-    name = "go_default_test",
-    srcs = ["config_test.go"],
-    embed = [":go_default_library"],
-    deps = [
-        "//go/lib/infra/modules/idiscovery:go_default_library",
-        "@com_github_burntsushi_toml//:go_default_library",
-        "@com_github_stretchr_testify//assert:go_default_library",
-    ],
-)
diff --git a/go/lib/infra/modules/idiscovery/idiscoverytest/config.go b/go/lib/infra/modules/idiscovery/idiscoverytest/config.go
deleted file mode 100644
index cda8cc54a..000000000
--- a/go/lib/infra/modules/idiscovery/idiscoverytest/config.go
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2019 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package idiscoverytest
-
-import (
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-
-	"github.com/scionproto/scion/go/lib/infra/modules/idiscovery"
-)
-
-func InitTestConfig(cfg *idiscovery.Config) {
-	cfg.Dynamic.Enable = true
-	cfg.Dynamic.Https = true
-	cfg.Static.Enable = true
-	cfg.Static.Https = true
-	cfg.Static.Filename = "topology.json"
-}
-
-func CheckTestConfig(t *testing.T, cfg *idiscovery.Config) {
-	t.Run("static", func(t *testing.T) {
-		checkCommon(t, cfg.Static.FetchConfig)
-		assert.Empty(t, cfg.Static.Filename)
-	})
-	t.Run("dynamic", func(t *testing.T) {
-		checkCommon(t, cfg.Dynamic)
-	})
-}
-
-func checkCommon(t *testing.T, cfg idiscovery.FetchConfig) {
-	assert.False(t, cfg.Enable)
-	assert.Equal(t, idiscovery.DefaultFetchTimeout, cfg.Timeout.Duration)
-	assert.False(t, cfg.Https)
-	assert.Equal(t, idiscovery.DefaultInitialConnectPeriod, cfg.Connect.InitialPeriod.Duration)
-	assert.Equal(t, idiscovery.FailActionContinue, cfg.Connect.FailAction)
-}
diff --git a/go/lib/infra/modules/idiscovery/idiscoverytest/config_test.go b/go/lib/infra/modules/idiscovery/idiscoverytest/config_test.go
deleted file mode 100644
index 6a4ebdbae..000000000
--- a/go/lib/infra/modules/idiscovery/idiscoverytest/config_test.go
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2019 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package idiscoverytest
-
-import (
-	"bytes"
-	"testing"
-
-	"github.com/BurntSushi/toml"
-	"github.com/stretchr/testify/assert"
-
-	"github.com/scionproto/scion/go/lib/infra/modules/idiscovery"
-)
-
-func TestConfigSample(t *testing.T) {
-	var sample bytes.Buffer
-	var cfg idiscovery.Config
-	cfg.Sample(&sample, nil, nil)
-	InitTestConfig(&cfg)
-	meta, err := toml.Decode(sample.String(), &cfg)
-	assert.NoError(t, err)
-	assert.Empty(t, meta.Undecoded())
-	CheckTestConfig(t, &cfg)
-}
diff --git a/go/lib/infra/modules/itopo/itopo_test.go b/go/lib/infra/modules/itopo/itopo_test.go
index fa405fd52..ee887503f 100644
--- a/go/lib/infra/modules/itopo/itopo_test.go
+++ b/go/lib/infra/modules/itopo/itopo_test.go
@@ -32,7 +32,7 @@ type updateTestFunc func(*topology.RWTopology) (*topology.RWTopology, bool, erro
 
 func setStaticTestFunc(s *state) updateTestFunc {
 	return func(t *topology.RWTopology) (*topology.RWTopology, bool, error) {
-		return s.setStatic(t, false)
+		return s.setStatic(t)
 	}
 }
 
@@ -50,8 +50,6 @@ func TestStateSetStatic(t *testing.T) {
 		clbks := newMockClbks(mctrl)
 		s := newState("", proto.ServiceType_unset, clbks.Clbks())
 		s.topo.static = loadTopo(fn, t)
-		// Set dynamic such that drop dynamic might possibly be called.
-		s.topo.dynamic = loadTopo(fn, t)
 		topo := loadTopo(fn, t)
 		Convey("Calling with modified topo should succeed", func() {
 			var wg xtest.Waiter
@@ -60,11 +58,10 @@ func TestStateSetStatic(t *testing.T) {
 			ifinfo.MTU = 42
 			topo.IFInfoMap[1] = ifinfo
 			clbks.update.EXPECT().Call().Do(wg.Done)
-			newTopo, updated, err := s.setStatic(topo, true)
+			_, updated, err := s.setStatic(topo)
 			SoMsg("err", err, ShouldBeNil)
 			SoMsg("updated", updated, ShouldBeTrue)
 			SoMsg("topo static", topo, ShouldEqual, s.topo.static)
-			SoMsg("topo dynamic", newTopo, ShouldEqual, s.topo.dynamic)
 			wg.WaitWithTimeout(time.Second)
 		})
 		testNilTopo(setStaticTestFunc(s), t)
@@ -75,8 +72,6 @@ func TestStateSetStatic(t *testing.T) {
 		id := "cs1-ff00:0:311-1"
 		s := newState(id, proto.ServiceType_cs, clbks.Clbks())
 		s.topo.static = loadTopo(fn, t)
-		// Set dynamic such that drop dynamic might possibly be called.
-		s.topo.dynamic = loadTopo(fn, t)
 		topo := loadTopo(fn, t)
 		Convey("Modification without touching the element's entry should be allowed", func() {
 			var wg xtest.Waiter
@@ -90,18 +85,17 @@ func TestStateSetStatic(t *testing.T) {
 			cs.UnderlayAddress = &net.UDPAddr{Port: 42}
 			topo.CS["cs1-ff00:0:311-2"] = cs
 			clbks.update.EXPECT().Call().Do(wg.Done)
-			newTopo, updated, err := s.setStatic(topo, true)
+			_, updated, err := s.setStatic(topo)
 			SoMsg("err", err, ShouldBeNil)
 			SoMsg("updated", updated, ShouldBeTrue)
 			SoMsg("topo static", topo, ShouldEqual, s.topo.static)
-			SoMsg("topo dynamic", newTopo, ShouldEqual, s.topo.dynamic)
 			wg.WaitWithTimeout(time.Second)
 		})
 		Convey("Modifying the element's entry should not be allowed", func() {
 			cs := topo.CS[id]
 			cs.UnderlayAddress = &net.UDPAddr{Port: 42}
 			topo.CS[id] = cs
-			_, updated, err := s.setStatic(topo, true)
+			_, updated, err := s.setStatic(topo)
 			SoMsg("err", err, ShouldNotBeNil)
 			SoMsg("updated", updated, ShouldBeFalse)
 			SoMsg("topo static", topo, ShouldNotEqual, s.topo.static)
@@ -114,8 +108,6 @@ func TestStateSetStatic(t *testing.T) {
 		id := "br1-ff00:0:311-1"
 		s := newState(id, proto.ServiceType_br, clbks.Clbks())
 		s.topo.static = loadTopo(fn, t)
-		// Set dynamic such that drop dynamic might possibly be called.
-		s.topo.dynamic = loadTopo(fn, t)
 		topo := loadTopo(fn, t)
 		Convey("Modification without touching the br's entry should be allowed", func() {
 			var wg xtest.Waiter
@@ -130,161 +122,44 @@ func TestStateSetStatic(t *testing.T) {
 			topo.CS["cs1-ff00:0:311-2"] = cs
 			Convey("If semi-mutation is allowed", func() {
 				clbks.update.EXPECT().Call().Do(wg.Done)
-				newTopo, updated, err := s.setStatic(topo, true)
+				_, updated, err := s.setStatic(topo)
 				SoMsg("err", err, ShouldBeNil)
 				SoMsg("updated", updated, ShouldBeTrue)
 				SoMsg("topo static", topo, ShouldEqual, s.topo.static)
-				SoMsg("topo dynamic", newTopo, ShouldEqual, s.topo.dynamic)
 				wg.WaitWithTimeout(time.Second)
 			})
 			Convey("If semi-mutation is not allowed", func() {
 				clbks.update.EXPECT().Call().Do(wg.Done)
-				newTopo, updated, err := s.setStatic(topo, false)
+				_, updated, err := s.setStatic(topo)
 				SoMsg("err", err, ShouldBeNil)
 				SoMsg("updated", updated, ShouldBeTrue)
 				SoMsg("topo static", topo, ShouldEqual, s.topo.static)
-				SoMsg("topo dynamic", newTopo, ShouldEqual, s.topo.dynamic)
 				wg.WaitWithTimeout(time.Second)
 			})
 		})
 		Convey("Modifying the internal address is not allowed", func() {
 			topo.BR[id].InternalAddr.Port = 42
-			Convey("If semi-mutation is allowed", func() {
-				_, updated, err := s.setStatic(topo, true)
-				SoMsg("err", err, ShouldNotBeNil)
-				SoMsg("updated", updated, ShouldBeFalse)
-				SoMsg("topo static", topo, ShouldNotEqual, s.topo.static)
-			})
-			Convey("If semi-mutation is not allowed", func() {
-				_, updated, err := s.setStatic(topo, false)
-				SoMsg("err", err, ShouldNotBeNil)
-				SoMsg("updated", updated, ShouldBeFalse)
-				SoMsg("topo static", topo, ShouldNotEqual, s.topo.static)
-			})
+			_, updated, err := s.setStatic(topo)
+			SoMsg("err", err, ShouldNotBeNil)
+			SoMsg("updated", updated, ShouldBeFalse)
+			SoMsg("topo static", topo, ShouldNotEqual, s.topo.static)
 		})
 		Convey("Modifying an interface is only allowed if semi-mutations are allowed", func() {
 			ifinfo := topo.IFInfoMap[1]
 			ifinfo.MTU = 42
 			topo.IFInfoMap[1] = ifinfo
-			Convey("Succeed, if semi-mutation is allowed", func() {
-				var wg xtest.Waiter
-				wg.Add(2)
-				clbks.update.EXPECT().Call().Do(wg.Done)
-				clbks.drop.EXPECT().Call().Do(wg.Done)
-				newTopo, updated, err := s.setStatic(topo, true)
-				SoMsg("err", err, ShouldBeNil)
-				SoMsg("updated", updated, ShouldBeTrue)
-				SoMsg("topo", newTopo, ShouldEqual, topo)
-				wg.WaitWithTimeout(time.Second)
-			})
-			Convey("Fail, if semi-mutation is not allowed", func() {
-				_, updated, err := s.setStatic(topo, false)
-				SoMsg("err", err, ShouldNotBeNil)
-				SoMsg("updated", updated, ShouldBeFalse)
-				SoMsg("topo static", topo, ShouldNotEqual, s.topo.static)
-			})
-		})
-		testNilTopo(setStaticTestFunc(s), t)
-		testNoModified(setStaticTestFunc(s), s.topo.static, clbks, true, t)
-	})
-}
 
-func TestStateSetDynamic(t *testing.T) {
-	mctrl := gomock.NewController(t)
-	defer mctrl.Finish()
-	Convey("When state is initialized with no specific element", t, func() {
-		clbks := newMockClbks(mctrl)
-		s := newState("", proto.ServiceType_unset, clbks.Clbks())
-		s.topo.static = loadTopo(fn, t)
-		// Set dynamic such that drop dynamic might possibly be called.
-		s.topo.dynamic = loadTopo(fn, t)
-		topo := loadTopo(fn, t)
-		topo.Timestamp = time.Now()
-		Convey("Calling with modified topo should succeed", func() {
-			ifinfo := topo.IFInfoMap[1]
-			ifinfo.MTU = 42
-			topo.IFInfoMap[1] = ifinfo
-			newTopo, updated, err := s.setDynamic(topo)
-			SoMsg("err", err, ShouldBeNil)
-			SoMsg("updated", updated, ShouldBeTrue)
-			SoMsg("topo", newTopo, ShouldEqual, topo)
-		})
-		testNilTopo(s.setDynamic, t)
-		testNoModified(s.setDynamic, s.topo.dynamic, clbks, false, t)
-	})
-	Convey("When itopo is initialized with a service element", t, func() {
-		clbks := newMockClbks(mctrl)
-		id := "cs1-ff00:0:311-1"
-		s := newState(id, proto.ServiceType_cs, clbks.Clbks())
-		s.topo.static = loadTopo(fn, t)
-		// Set dynamic such that drop dynamic might possibly be called.
-		s.topo.dynamic = loadTopo(fn, t)
-		topo := loadTopo(fn, t)
-		topo.Timestamp = time.Now()
-		Convey("Modification without touching the element's entry should be allowed", func() {
-			// Modify border router
-			ifinfo := topo.IFInfoMap[1]
-			ifinfo.MTU = 42
-			topo.IFInfoMap[1] = ifinfo
-			// modify other cs
-			cs := topo.CS["cs1-ff00:0:311-2"]
-			cs.UnderlayAddress = &net.UDPAddr{Port: 42}
-			topo.CS["cs1-ff00:0:311-2"] = cs
-			newTopo, updated, err := s.setDynamic(topo)
-			SoMsg("err", err, ShouldBeNil)
-			SoMsg("updated", updated, ShouldBeTrue)
-			SoMsg("topo", newTopo, ShouldEqual, topo)
-		})
-		Convey("Modifying the element's entry should not be allowed", func() {
-			cs := topo.CS[id]
-			cs.UnderlayAddress = &net.UDPAddr{Port: 42}
-			topo.CS[id] = cs
-			_, updated, err := s.setDynamic(topo)
-			SoMsg("err", err, ShouldNotBeNil)
-			SoMsg("updated", updated, ShouldBeFalse)
-		})
-		testNilTopo(s.setDynamic, t)
-		testNoModified(s.setDynamic, s.topo.dynamic, clbks, false, t)
-	})
-	Convey("When itopo is initialized with a border router", t, func() {
-		clbks := newMockClbks(mctrl)
-		id := "br1-ff00:0:311-1"
-		s := newState(id, proto.ServiceType_br, clbks.Clbks())
-		s.topo.static = loadTopo(fn, t)
-		// Set dynamic such that drop dynamic might possibly be called.
-		s.topo.dynamic = loadTopo(fn, t)
-		topo := loadTopo(fn, t)
-		topo.Timestamp = time.Now()
-		Convey("Modification without touching the br's entry should be allowed", func() {
-			// Modify border router
-			ifinfo := topo.IFInfoMap[2]
-			ifinfo.MTU = 42
-			topo.IFInfoMap[2] = ifinfo
-			// modify other cs
-			cs := topo.CS["cs1-ff00:0:311-2"]
-			cs.UnderlayAddress = &net.UDPAddr{Port: 42}
-			topo.CS["cs1-ff00:0:311-2"] = cs
-			newTopo, updated, err := s.setDynamic(topo)
+			var wg xtest.Waiter
+			wg.Add(2)
+			clbks.update.EXPECT().Call().Do(wg.Done)
+			newTopo, updated, err := s.setStatic(topo)
 			SoMsg("err", err, ShouldBeNil)
 			SoMsg("updated", updated, ShouldBeTrue)
 			SoMsg("topo", newTopo, ShouldEqual, topo)
+			wg.WaitWithTimeout(time.Second)
 		})
-		Convey("Modifying the internal address is not allowed", func() {
-			topo.BR[id].InternalAddr.Port = 42
-			_, updated, err := s.setDynamic(topo)
-			SoMsg("err", err, ShouldNotBeNil)
-			SoMsg("updated", updated, ShouldBeFalse)
-		})
-		Convey("Modifying an interface is not allowed", func() {
-			ifinfo := topo.IFInfoMap[1]
-			ifinfo.MTU = 42
-			topo.IFInfoMap[1] = ifinfo
-			_, updated, err := s.setDynamic(topo)
-			SoMsg("err", err, ShouldNotBeNil)
-			SoMsg("updated", updated, ShouldBeFalse)
-		})
-		testNilTopo(s.setDynamic, t)
-		testNoModified(s.setDynamic, s.topo.dynamic, clbks, false, t)
+		testNilTopo(setStaticTestFunc(s), t)
+		testNoModified(setStaticTestFunc(s), s.topo.static, clbks, true, t)
 	})
 }
 
@@ -330,23 +205,17 @@ func testNoModified(update updateTestFunc, prevTopo *topology.RWTopology,
 }
 
 type mockClbks struct {
-	clean  *mock_xtest.MockCallback
-	drop   *mock_xtest.MockCallback
 	update *mock_xtest.MockCallback
 }
 
 func newMockClbks(mctrl *gomock.Controller) *mockClbks {
 	return &mockClbks{
-		clean:  mock_xtest.NewMockCallback(mctrl),
-		drop:   mock_xtest.NewMockCallback(mctrl),
 		update: mock_xtest.NewMockCallback(mctrl),
 	}
 }
 
 func (clbks *mockClbks) Clbks() Callbacks {
 	return Callbacks{
-		CleanDynamic: clbks.clean.Call,
-		DropDynamic:  clbks.drop.Call,
 		UpdateStatic: clbks.update.Call,
 	}
 }
diff --git a/go/lib/infra/modules/itopo/validate_test.go b/go/lib/infra/modules/itopo/validate_test.go
index c9fc744f7..3b9fe5205 100644
--- a/go/lib/infra/modules/itopo/validate_test.go
+++ b/go/lib/infra/modules/itopo/validate_test.go
@@ -128,116 +128,6 @@ func TestBrValidatorImmutable(t *testing.T) {
 	})
 }
 
-func TestBrValidatorSemiMutable(t *testing.T) {
-	Convey("Given a border router validator", t, func() {
-		v := &brValidator{id: "br1-ff00:0:311-1"}
-		other := "br1-ff00:0:311-2"
-		oldTopo := loadTopo(fn, t)
-		topo := loadTopo(fn, t)
-		Convey("And semi-mutation is allowed", func() {
-			Convey("Adding a new interface is allowed", func() {
-				brInfo := topo.BR[v.id]
-				brInfo.IFIDs = append(brInfo.IFIDs, 42)
-				topo.BR[v.id] = brInfo
-				SoMsg("err", v.SemiMutable(topo, oldTopo, true), ShouldBeNil)
-			})
-			Convey("Modifying an interface is allowed", func() {
-				ifinfo := topo.IFInfoMap[1]
-				ifinfo.MTU = 42
-				topo.IFInfoMap[1] = ifinfo
-				SoMsg("err", v.SemiMutable(topo, oldTopo, true), ShouldBeNil)
-			})
-			Convey("Deleting an interface is allowed", func() {
-				brInfo := topo.BR[v.id]
-				brInfo.IFIDs = brInfo.IFIDs[1:]
-				topo.BR[v.id] = brInfo
-				SoMsg("err", v.SemiMutable(topo, oldTopo, true), ShouldBeNil)
-			})
-			Convey("Adding and deleting an interface is allowed", func() {
-				brInfo := topo.BR[v.id]
-				brInfo.IFIDs = append(brInfo.IFIDs, 42)
-				brInfo.IFIDs = brInfo.IFIDs[1:]
-				topo.BR[v.id] = brInfo
-				SoMsg("err", v.SemiMutable(topo, oldTopo, true), ShouldBeNil)
-			})
-			Convey("No modification is allowed", func() {
-				SoMsg("err", v.SemiMutable(topo, oldTopo, true), ShouldBeNil)
-			})
-			Convey("Modification of different br is allowed", func() {
-				brInfo := topo.BR[other]
-				brInfo.IFIDs = append(brInfo.IFIDs, 42)
-				brInfo.IFIDs = brInfo.IFIDs[1:]
-				topo.BR[other] = brInfo
-				SoMsg("err", v.SemiMutable(topo, oldTopo, true), ShouldBeNil)
-			})
-		})
-		Convey("And semi-mutation is not allowed", func() {
-			Convey("Adding a new interface is not allowed", func() {
-				brInfo := topo.BR[v.id]
-				brInfo.IFIDs = append(brInfo.IFIDs, 42)
-				topo.BR[v.id] = brInfo
-				SoMsg("err", v.SemiMutable(topo, oldTopo, false), ShouldNotBeNil)
-			})
-			Convey("Modifying an interface is not allowed", func() {
-				ifinfo := topo.IFInfoMap[1]
-				ifinfo.MTU = 42
-				topo.IFInfoMap[1] = ifinfo
-				SoMsg("err", v.SemiMutable(topo, oldTopo, false), ShouldNotBeNil)
-			})
-			Convey("Deleting an interface is not allowed", func() {
-				brInfo := topo.BR[v.id]
-				brInfo.IFIDs = brInfo.IFIDs[1:]
-				topo.BR[v.id] = brInfo
-				SoMsg("err", v.SemiMutable(topo, oldTopo, false), ShouldNotBeNil)
-			})
-			Convey("Adding and deleting an interface is not allowed", func() {
-				brInfo := topo.BR[v.id]
-				brInfo.IFIDs = append(brInfo.IFIDs, 42)
-				brInfo.IFIDs = brInfo.IFIDs[1:]
-				topo.BR[v.id] = brInfo
-				SoMsg("err", v.SemiMutable(topo, oldTopo, false), ShouldNotBeNil)
-			})
-			Convey("No modification is allowed", func() {
-				SoMsg("err", v.SemiMutable(topo, oldTopo, false), ShouldBeNil)
-			})
-			Convey("Modification of different br is allowed", func() {
-				brInfo := topo.BR[other]
-				brInfo.IFIDs = append(brInfo.IFIDs, 42)
-				brInfo.IFIDs = brInfo.IFIDs[1:]
-				topo.BR[other] = brInfo
-				SoMsg("err", v.SemiMutable(topo, oldTopo, false), ShouldBeNil)
-			})
-		})
-	})
-}
-
-func TestBrValidatorMustDropDynamic(t *testing.T) {
-	Convey("Given a border router validator", t, func() {
-		v := &brValidator{id: "br1-ff00:0:311-1"}
-		oldTopo := loadTopo(fn, t)
-		topo := loadTopo(fn, t)
-
-		Convey("Adding a new interface forces a drop", func() {
-			brInfo := topo.BR[v.id]
-			brInfo.IFIDs = append(brInfo.IFIDs, 42)
-			topo.BR[v.id] = brInfo
-			SoMsg("drop", v.MustDropDynamic(topo, oldTopo), ShouldBeTrue)
-		})
-		Convey("Modifying an interface forces a drop", func() {
-			ifinfo := topo.IFInfoMap[1]
-			ifinfo.MTU = 42
-			topo.IFInfoMap[1] = ifinfo
-			SoMsg("drop", v.MustDropDynamic(topo, oldTopo), ShouldBeTrue)
-		})
-		Convey("Deleting an interface forces a drop", func() {
-			brInfo := topo.BR[v.id]
-			brInfo.IFIDs = brInfo.IFIDs[1:]
-			topo.BR[v.id] = brInfo
-			SoMsg("drop", v.MustDropDynamic(topo, oldTopo), ShouldBeTrue)
-		})
-	})
-}
-
 func testGenImmutable(v internalValidator, topo, oldTopo *topology.RWTopology, t *testing.T) {
 	t.Helper()
 	Convey("Updating the IA is not allowed", func() {
diff --git a/go/lib/topology/topology_test.go b/go/lib/topology/topology_test.go
index 92eb998bb..8e852b2b5 100644
--- a/go/lib/topology/topology_test.go
+++ b/go/lib/topology/topology_test.go
@@ -119,7 +119,6 @@ func TestServiceCount(t *testing.T) {
 	c := MustLoadTopo(t, "testdata/basic.json")
 	assert.Len(t, c.CS, 2, "CS")
 	assert.Len(t, c.SIG, 2, "SIG")
-	assert.Len(t, c.DS, 2, "DS")
 }
 
 func TestIFInfoMap(t *testing.T) {
diff --git a/go/sciond/internal/config/config_test.go b/go/sciond/internal/config/config_test.go
index 600a74773..7e591b871 100644
--- a/go/sciond/internal/config/config_test.go
+++ b/go/sciond/internal/config/config_test.go
@@ -22,7 +22,6 @@ import (
 	"github.com/stretchr/testify/assert"
 
 	"github.com/scionproto/scion/go/lib/env/envtest"
-	"github.com/scionproto/scion/go/lib/infra/modules/idiscovery/idiscoverytest"
 	"github.com/scionproto/scion/go/lib/pathstorage/pathstoragetest"
 	"github.com/scionproto/scion/go/lib/sciond"
 	"github.com/scionproto/scion/go/lib/truststorage/truststoragetest"
@@ -43,7 +42,6 @@ func TestConfigSample(t *testing.T) {
 func InitTestConfig(cfg *Config) {
 	envtest.InitTest(&cfg.General, &cfg.Logging, &cfg.Metrics, &cfg.Tracing, nil)
 	truststoragetest.InitTestConfig(&cfg.TrustDB)
-	idiscoverytest.InitTestConfig(&cfg.Discovery)
 	InitTestSDConfig(&cfg.SD)
 }
 
@@ -55,7 +53,6 @@ func InitTestSDConfig(cfg *SDConfig) {
 func CheckTestConfig(t *testing.T, cfg *Config, id string) {
 	envtest.CheckTest(t, &cfg.General, &cfg.Logging, &cfg.Metrics, &cfg.Tracing, nil, id)
 	truststoragetest.CheckTestConfig(t, &cfg.TrustDB, id)
-	idiscoverytest.CheckTestConfig(t, &cfg.Discovery)
 	CheckTestSDConfig(t, &cfg.SD, id)
 }
 
diff --git a/python/test/lib/topology_test.py b/python/test/lib/topology_test.py
index b000a73fb..b7029af9f 100644
--- a/python/test/lib/topology_test.py
+++ b/python/test/lib/topology_test.py
@@ -161,7 +161,6 @@ class TestTopologyParseSrvDicts(object):
         topo_dict = {
             'ControlService': {"cs1": "cs1 val"},
             'SIG': {"sig1": "sig1 val"},
-            'DiscoveryService': {"ds1": "ds1 val"},
         }
         inst = Topology()
         server.side_effect = lambda v, k: "%s-%s" % (k, v)
@@ -171,10 +170,8 @@ class TestTopologyParseSrvDicts(object):
         assert_these_calls(server, [
             call("cs1 val", "cs1"),
             call("sig1 val", "sig1"),
-            call("ds1 val", "ds1"),
         ], any_order=True)
         ntools.eq_(inst.control_servers, ["cs1-cs1 val"])
-        ntools.eq_(inst.discovery_servers, ["ds1-ds1 val"])
 
 
 class TestTopologyParseRouterDicts(object):