Repository: scionproto/scion
Pull request #3785 (2020-06-12 14:06:23)

sciond: remove global state
Remove the gloabl state from sciond main.

Functionality other then initialization is moved to `go/pkg/sciond`.
The remaining global state hides in itopo.

The command is moved to cobra, and the CLI is changed to fluent style
instead of the previous flag style for sub-commands:

    sciond --config sd.toml
    sciond help-config
    sciond version

Note that `-config` is no longer a valid flag

<!-- 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/3785)
<!-- Reviewable:end -->



Patch:
diff --git a/acceptance/topo_sd_reload/BUILD.bazel b/acceptance/topo_sd_reload/BUILD.bazel
index 6d1720934..416f927b5 100644
--- a/acceptance/topo_sd_reload/BUILD.bazel
+++ b/acceptance/topo_sd_reload/BUILD.bazel
@@ -24,7 +24,7 @@ container_image(
     base = "//docker:dispatcher_debug",
     entrypoint = [
         "/app/dispatcher",
-        "-config",
+        "--config",
         "/disp.toml",
     ],
     files = ["testdata/disp.toml"],
@@ -35,7 +35,7 @@ container_image(
     base = "//docker:sciond_debug",
     entrypoint = [
         "/app/sciond",
-        "-config",
+        "--config",
         "/sd.toml",
     ],
     files = [
diff --git a/docker/BUILD.bazel b/docker/BUILD.bazel
index a0458b4c2..2ab44e683 100644
--- a/docker/BUILD.bazel
+++ b/docker/BUILD.bazel
@@ -45,7 +45,7 @@ scion_app_images(
     binary = "//go/border:border",
     entrypoint = [
         "/app/border",
-        "-config",
+        "--config",
         "/share/conf/br.toml",
     ],
     workdir = "/share",
@@ -57,7 +57,7 @@ scion_app_images(
     binary = "//go/cs:cs",
     entrypoint = [
         "/app/cs",
-        "-config",
+        "--config",
         "/share/conf/cs.toml",
     ],
     workdir = "/share",
@@ -69,7 +69,7 @@ scion_app_images(
     binary = "//go/dispatcher:dispatcher",
     entrypoint = [
         "/app/dispatcher",
-        "-config",
+        "--config",
         "/share/conf/disp.toml",
     ],
     workdir = "/share",
@@ -81,7 +81,7 @@ scion_app_images(
     binary = "//go/sciond:sciond",
     entrypoint = [
         "/app/sciond",
-        "-config",
+        "--config",
         "/share/conf/sd.toml",
     ],
     workdir = "/share",
@@ -93,7 +93,7 @@ scion_app_images(
     binary = "//go/sig:sig",
     entrypoint = [
         "/app/sig",
-        "-config",
+        "--config",
         "/share/conf/sig.toml",
     ],
     workdir = "/share",
diff --git a/go/pkg/sciond/BUILD.bazel b/go/pkg/sciond/BUILD.bazel
new file mode 100644
index 000000000..3e93b08c0
--- /dev/null
+++ b/go/pkg/sciond/BUILD.bazel
@@ -0,0 +1,26 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+    name = "go_default_library",
+    srcs = ["sciond.go"],
+    importpath = "github.com/scionproto/scion/go/pkg/sciond",
+    visibility = ["//visibility:public"],
+    deps = [
+        "//go/lib/env:go_default_library",
+        "//go/lib/infra/messenger/tcp:go_default_library",
+        "//go/lib/infra/modules/itopo:go_default_library",
+        "//go/lib/infra/modules/segfetcher:go_default_library",
+        "//go/lib/log:go_default_library",
+        "//go/lib/pathdb:go_default_library",
+        "//go/lib/revcache:go_default_library",
+        "//go/lib/serrors:go_default_library",
+        "//go/pkg/sciond/config:go_default_library",
+        "//go/pkg/sciond/fetcher:go_default_library",
+        "//go/pkg/sciond/internal/servers:go_default_library",
+        "//go/pkg/trust:go_default_library",
+        "//go/pkg/trust/compat:go_default_library",
+        "//go/proto:go_default_library",
+        "@com_github_burntsushi_toml//:go_default_library",
+        "@com_github_opentracing_opentracing_go//:go_default_library",
+    ],
+)
diff --git a/go/sciond/config/BUILD.bazel b/go/pkg/sciond/config/BUILD.bazel
similarity index 94%
rename from go/sciond/config/BUILD.bazel
rename to go/pkg/sciond/config/BUILD.bazel
index 12e512b0d..c368634b9 100644
--- a/go/sciond/config/BUILD.bazel
+++ b/go/pkg/sciond/config/BUILD.bazel
@@ -6,7 +6,7 @@ go_library(
         "config.go",
         "sample.go",
     ],
-    importpath = "github.com/scionproto/scion/go/sciond/config",
+    importpath = "github.com/scionproto/scion/go/pkg/sciond/config",
     visibility = ["//visibility:public"],
     deps = [
         "//go/lib/config:go_default_library",
diff --git a/go/sciond/config/config.go b/go/pkg/sciond/config/config.go
similarity index 100%
rename from go/sciond/config/config.go
rename to go/pkg/sciond/config/config.go
diff --git a/go/sciond/config/config_test.go b/go/pkg/sciond/config/config_test.go
similarity index 100%
rename from go/sciond/config/config_test.go
rename to go/pkg/sciond/config/config_test.go
diff --git a/go/sciond/config/sample.go b/go/pkg/sciond/config/sample.go
similarity index 100%
rename from go/sciond/config/sample.go
rename to go/pkg/sciond/config/sample.go
diff --git a/go/sciond/internal/fetcher/BUILD.bazel b/go/pkg/sciond/fetcher/BUILD.bazel
similarity index 84%
rename from go/sciond/internal/fetcher/BUILD.bazel
rename to go/pkg/sciond/fetcher/BUILD.bazel
index 71ce1030b..3db9b7d00 100644
--- a/go/sciond/internal/fetcher/BUILD.bazel
+++ b/go/pkg/sciond/fetcher/BUILD.bazel
@@ -7,8 +7,8 @@ go_library(
         "filter.go",
         "pathmeta.go",
     ],
-    importpath = "github.com/scionproto/scion/go/sciond/internal/fetcher",
-    visibility = ["//go/sciond:__subpackages__"],
+    importpath = "github.com/scionproto/scion/go/pkg/sciond/fetcher",
+    visibility = ["//visibility:public"],
     deps = [
         "//go/lib/addr:go_default_library",
         "//go/lib/hostinfo:go_default_library",
@@ -25,10 +25,10 @@ go_library(
         "//go/lib/spath:go_default_library",
         "//go/lib/topology:go_default_library",
         "//go/lib/util:go_default_library",
+        "//go/pkg/sciond/config:go_default_library",
+        "//go/pkg/sciond/internal/metrics:go_default_library",
         "//go/pkg/trust:go_default_library",
         "//go/proto:go_default_library",
-        "//go/sciond/config:go_default_library",
-        "//go/sciond/internal/metrics:go_default_library",
     ],
 )
 
@@ -47,7 +47,7 @@ go_test(
         "//go/lib/pathpol:go_default_library",
         "//go/lib/xtest:go_default_library",
         "//go/lib/xtest/graph:go_default_library",
-        "//go/sciond/internal/fetcher/mock_fetcher:go_default_library",
+        "//go/pkg/sciond/fetcher/mock_fetcher:go_default_library",
         "@com_github_golang_mock//gomock:go_default_library",
         "@com_github_stretchr_testify//assert:go_default_library",
     ],
diff --git a/go/sciond/internal/fetcher/fetcher.go b/go/pkg/sciond/fetcher/fetcher.go
similarity index 98%
rename from go/sciond/internal/fetcher/fetcher.go
rename to go/pkg/sciond/fetcher/fetcher.go
index 83fdfe14c..a9bc54663 100644
--- a/go/sciond/internal/fetcher/fetcher.go
+++ b/go/pkg/sciond/fetcher/fetcher.go
@@ -36,9 +36,9 @@ import (
 	"github.com/scionproto/scion/go/lib/spath"
 	"github.com/scionproto/scion/go/lib/topology"
 	"github.com/scionproto/scion/go/lib/util"
+	"github.com/scionproto/scion/go/pkg/sciond/config"
+	"github.com/scionproto/scion/go/pkg/sciond/internal/metrics"
 	"github.com/scionproto/scion/go/pkg/trust"
-	"github.com/scionproto/scion/go/sciond/config"
-	"github.com/scionproto/scion/go/sciond/internal/metrics"
 )
 
 const (
diff --git a/go/sciond/internal/fetcher/mock_fetcher/BUILD.bazel b/go/pkg/sciond/fetcher/mock_fetcher/BUILD.bazel
similarity index 60%
rename from go/sciond/internal/fetcher/mock_fetcher/BUILD.bazel
rename to go/pkg/sciond/fetcher/mock_fetcher/BUILD.bazel
index 858f7bb68..7fa607aa2 100644
--- a/go/sciond/internal/fetcher/mock_fetcher/BUILD.bazel
+++ b/go/pkg/sciond/fetcher/mock_fetcher/BUILD.bazel
@@ -3,10 +3,11 @@ 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/sciond/internal/fetcher/mock_fetcher",
-    visibility = ["//go/sciond:__subpackages__"],
+    importpath = "github.com/scionproto/scion/go/pkg/sciond/fetcher/mock_fetcher",
+    visibility = ["//visibility:public"],
     deps = [
         "//go/lib/pathpol:go_default_library",
+        "//go/lib/sciond:go_default_library",
         "@com_github_golang_mock//gomock:go_default_library",
     ],
 )
diff --git a/go/pkg/sciond/fetcher/mock_fetcher/fetcher.go b/go/pkg/sciond/fetcher/mock_fetcher/fetcher.go
new file mode 100644
index 000000000..05241a434
--- /dev/null
+++ b/go/pkg/sciond/fetcher/mock_fetcher/fetcher.go
@@ -0,0 +1,89 @@
+// Code generated by MockGen. DO NOT EDIT.
+// Source: github.com/scionproto/scion/go/pkg/sciond/fetcher (interfaces: Fetcher,Policy)
+
+// Package mock_fetcher is a generated GoMock package.
+package mock_fetcher
+
+import (
+	context "context"
+	gomock "github.com/golang/mock/gomock"
+	pathpol "github.com/scionproto/scion/go/lib/pathpol"
+	sciond "github.com/scionproto/scion/go/lib/sciond"
+	reflect "reflect"
+	time "time"
+)
+
+// MockFetcher is a mock of Fetcher interface
+type MockFetcher struct {
+	ctrl     *gomock.Controller
+	recorder *MockFetcherMockRecorder
+}
+
+// MockFetcherMockRecorder is the mock recorder for MockFetcher
+type MockFetcherMockRecorder struct {
+	mock *MockFetcher
+}
+
+// NewMockFetcher creates a new mock instance
+func NewMockFetcher(ctrl *gomock.Controller) *MockFetcher {
+	mock := &MockFetcher{ctrl: ctrl}
+	mock.recorder = &MockFetcherMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use
+func (m *MockFetcher) EXPECT() *MockFetcherMockRecorder {
+	return m.recorder
+}
+
+// GetPaths mocks base method
+func (m *MockFetcher) GetPaths(arg0 context.Context, arg1 *sciond.PathReq, arg2 time.Duration) (*sciond.PathReply, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetPaths", arg0, arg1, arg2)
+	ret0, _ := ret[0].(*sciond.PathReply)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// GetPaths indicates an expected call of GetPaths
+func (mr *MockFetcherMockRecorder) GetPaths(arg0, arg1, arg2 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPaths", reflect.TypeOf((*MockFetcher)(nil).GetPaths), arg0, arg1, arg2)
+}
+
+// MockPolicy is a mock of Policy interface
+type MockPolicy struct {
+	ctrl     *gomock.Controller
+	recorder *MockPolicyMockRecorder
+}
+
+// MockPolicyMockRecorder is the mock recorder for MockPolicy
+type MockPolicyMockRecorder struct {
+	mock *MockPolicy
+}
+
+// NewMockPolicy creates a new mock instance
+func NewMockPolicy(ctrl *gomock.Controller) *MockPolicy {
+	mock := &MockPolicy{ctrl: ctrl}
+	mock.recorder = &MockPolicyMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use
+func (m *MockPolicy) EXPECT() *MockPolicyMockRecorder {
+	return m.recorder
+}
+
+// Filter mocks base method
+func (m *MockPolicy) Filter(arg0 pathpol.PathSet) pathpol.PathSet {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Filter", arg0)
+	ret0, _ := ret[0].(pathpol.PathSet)
+	return ret0
+}
+
+// Filter indicates an expected call of Filter
+func (mr *MockPolicyMockRecorder) Filter(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Filter", reflect.TypeOf((*MockPolicy)(nil).Filter), arg0)
+}
diff --git a/go/sciond/internal/fetcher/pathmeta.go b/go/pkg/sciond/fetcher/pathmeta.go
similarity index 100%
rename from go/sciond/internal/fetcher/pathmeta.go
rename to go/pkg/sciond/fetcher/pathmeta.go
diff --git a/go/sciond/internal/fetcher/pathmeta_test.go b/go/pkg/sciond/fetcher/pathmeta_test.go
similarity index 100%
rename from go/sciond/internal/fetcher/pathmeta_test.go
rename to go/pkg/sciond/fetcher/pathmeta_test.go
diff --git a/go/sciond/internal/metrics/BUILD.bazel b/go/pkg/sciond/internal/metrics/BUILD.bazel
similarity index 78%
rename from go/sciond/internal/metrics/BUILD.bazel
rename to go/pkg/sciond/internal/metrics/BUILD.bazel
index a9563709a..e176e65dd 100644
--- a/go/sciond/internal/metrics/BUILD.bazel
+++ b/go/pkg/sciond/internal/metrics/BUILD.bazel
@@ -3,8 +3,8 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
 go_library(
     name = "go_default_library",
     srcs = ["metrics.go"],
-    importpath = "github.com/scionproto/scion/go/sciond/internal/metrics",
-    visibility = ["//go/sciond:__subpackages__"],
+    importpath = "github.com/scionproto/scion/go/pkg/sciond/internal/metrics",
+    visibility = ["//go/pkg/sciond:__subpackages__"],
     deps = [
         "//go/lib/addr:go_default_library",
         "//go/lib/prom:go_default_library",
diff --git a/go/sciond/internal/servers/BUILD.bazel b/go/pkg/sciond/internal/servers/BUILD.bazel
similarity index 81%
rename from go/sciond/internal/servers/BUILD.bazel
rename to go/pkg/sciond/internal/servers/BUILD.bazel
index a3c86459b..288add9f8 100644
--- a/go/sciond/internal/servers/BUILD.bazel
+++ b/go/pkg/sciond/internal/servers/BUILD.bazel
@@ -7,8 +7,8 @@ go_library(
         "handlers.go",
         "server.go",
     ],
-    importpath = "github.com/scionproto/scion/go/sciond/internal/servers",
-    visibility = ["//go/sciond:__subpackages__"],
+    importpath = "github.com/scionproto/scion/go/pkg/sciond/internal/servers",
+    visibility = ["//go/pkg/sciond:__subpackages__"],
     deps = [
         "//go/lib/common:go_default_library",
         "//go/lib/ctrl/path_mgmt:go_default_library",
@@ -22,10 +22,10 @@ go_library(
         "//go/lib/sciond:go_default_library",
         "//go/lib/serrors:go_default_library",
         "//go/lib/tracing:go_default_library",
+        "//go/pkg/sciond/fetcher:go_default_library",
+        "//go/pkg/sciond/internal/metrics:go_default_library",
         "//go/pkg/trust:go_default_library",
         "//go/proto:go_default_library",
-        "//go/sciond/internal/fetcher:go_default_library",
-        "//go/sciond/internal/metrics:go_default_library",
         "@com_github_opentracing_opentracing_go//:go_default_library",
         "@com_github_opentracing_opentracing_go//ext:go_default_library",
         "@com_zombiezen_go_capnproto2//:go_default_library",
diff --git a/go/sciond/internal/servers/api.go b/go/pkg/sciond/internal/servers/api.go
similarity index 100%
rename from go/sciond/internal/servers/api.go
rename to go/pkg/sciond/internal/servers/api.go
diff --git a/go/sciond/internal/servers/handlers.go b/go/pkg/sciond/internal/servers/handlers.go
similarity index 99%
rename from go/sciond/internal/servers/handlers.go
rename to go/pkg/sciond/internal/servers/handlers.go
index e8287d054..5233a9153 100644
--- a/go/sciond/internal/servers/handlers.go
+++ b/go/pkg/sciond/internal/servers/handlers.go
@@ -30,10 +30,10 @@ import (
 	"github.com/scionproto/scion/go/lib/revcache"
 	"github.com/scionproto/scion/go/lib/sciond"
 	"github.com/scionproto/scion/go/lib/serrors"
+	"github.com/scionproto/scion/go/pkg/sciond/fetcher"
+	"github.com/scionproto/scion/go/pkg/sciond/internal/metrics"
 	"github.com/scionproto/scion/go/pkg/trust"
 	"github.com/scionproto/scion/go/proto"
-	"github.com/scionproto/scion/go/sciond/internal/fetcher"
-	"github.com/scionproto/scion/go/sciond/internal/metrics"
 )
 
 const (
diff --git a/go/sciond/internal/servers/server.go b/go/pkg/sciond/internal/servers/server.go
similarity index 100%
rename from go/sciond/internal/servers/server.go
rename to go/pkg/sciond/internal/servers/server.go
diff --git a/go/pkg/sciond/sciond.go b/go/pkg/sciond/sciond.go
new file mode 100644
index 000000000..b75c11cbd
--- /dev/null
+++ b/go/pkg/sciond/sciond.go
@@ -0,0 +1,129 @@
+// Copyright 2018 ETH Zurich, 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 sciond
+
+import (
+	"bytes"
+	"context"
+	"fmt"
+	"io"
+	"net/http"
+	"path/filepath"
+
+	"github.com/BurntSushi/toml"
+	"github.com/opentracing/opentracing-go"
+
+	"github.com/scionproto/scion/go/lib/env"
+	"github.com/scionproto/scion/go/lib/infra/messenger/tcp"
+	"github.com/scionproto/scion/go/lib/infra/modules/itopo"
+	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher"
+	"github.com/scionproto/scion/go/lib/log"
+	"github.com/scionproto/scion/go/lib/pathdb"
+	"github.com/scionproto/scion/go/lib/revcache"
+	"github.com/scionproto/scion/go/lib/serrors"
+	"github.com/scionproto/scion/go/pkg/sciond/config"
+	"github.com/scionproto/scion/go/pkg/sciond/fetcher"
+	"github.com/scionproto/scion/go/pkg/sciond/internal/servers"
+	"github.com/scionproto/scion/go/pkg/trust"
+	"github.com/scionproto/scion/go/pkg/trust/compat"
+	"github.com/scionproto/scion/go/proto"
+)
+
+// InitTracer initializes the global tracer.
+func InitTracer(cfg config.Config) (io.Closer, error) {
+	tracer, trCloser, err := cfg.Tracing.NewTracer(cfg.General.ID)
+	if err != nil {
+		return nil, err
+	}
+	opentracing.SetGlobalTracer(tracer)
+	return trCloser, nil
+}
+
+// TrustEngine builds the trust engine backed by the trust database.
+func TrustEngine(cfgDir string, db trust.DB) (trust.Engine, error) {
+	certsDir := filepath.Join(cfgDir, "certs")
+	loaded, err := trust.LoadTRCs(context.Background(), certsDir, db)
+	if err != nil {
+		return trust.Engine{}, serrors.WrapStr("loading TRCs", err)
+	}
+	log.Info("TRCs loaded", "files", loaded.Loaded)
+	for f, r := range loaded.Ignored {
+		log.Info("Ignoring non-TRC", "file", f, "reason", r)
+	}
+	loaded, err = trust.LoadChains(context.Background(), certsDir, db)
+	if err != nil {
+		return trust.Engine{}, serrors.WrapStr("loading certificate chains",
+			err)
+	}
+	log.Info("Certificate chains loaded", "files", loaded.Loaded)
+	for f, r := range loaded.Ignored {
+		log.Info("Ignoring non-certificate chain", "file", f, "reason", r)
+	}
+	return trust.Engine{
+		Inspector: trust.DBInspector{DB: db},
+		Provider: trust.FetchingProvider{
+			DB: db,
+			Fetcher: trust.DefaultFetcher{
+				RPC: tcp.NewClientMessenger(),
+				IA:  itopo.Get().IA(),
+			},
+			Recurser: trust.LocalOnlyRecurser{},
+			Router:   trust.LocalRouter{IA: itopo.Get().IA()},
+		},
+		DB: db,
+	}, nil
+}
+
+// ServerCfg is the configuration for the API server.
+type ServerCfg struct {
+	Fetcher  fetcher.Fetcher
+	PathDB   pathdb.PathDB
+	RevCache revcache.RevCache
+	Engine   trust.Engine
+}
+
+// Server constructs a API server. The caller is responsible for starting and
+// shutting it down.
+func Server(listen string, cfg ServerCfg) *servers.Server {
+	handlers := servers.HandlerMap{
+		proto.SCIONDMsg_Which_pathReq: &servers.PathRequestHandler{
+			Fetcher: cfg.Fetcher,
+		},
+		proto.SCIONDMsg_Which_asInfoReq: &servers.ASInfoRequestHandler{
+			ASInspector: cfg.Engine,
+		},
+		proto.SCIONDMsg_Which_ifInfoRequest:      &servers.IFInfoRequestHandler{},
+		proto.SCIONDMsg_Which_serviceInfoRequest: &servers.SVCInfoRequestHandler{},
+		proto.SCIONDMsg_Which_revNotification: &servers.RevNotificationHandler{
+			RevCache:         cfg.RevCache,
+			Verifier:         compat.Verifier{Verifier: trust.Verifier{Engine: cfg.Engine}},
+			NextQueryCleaner: segfetcher.NextQueryCleaner{PathDB: cfg.PathDB},
+		},
+	}
+	return servers.NewServer("tcp", listen, handlers)
+}
+
+// StartHTTPEndpoints starts the HTTP endpoints.
+func StartHTTPEndpoints(cfg interface{}, metrics env.Metrics) {
+	http.HandleFunc("/config", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Content-Type", "text/plain")
+		var buf bytes.Buffer
+		toml.NewEncoder(&buf).Encode(cfg)
+		fmt.Fprint(w, buf.String())
+	})
+	http.HandleFunc("/info", env.InfoHandler)
+	http.HandleFunc("/topology", itopo.TopologyHandler)
+	metrics.StartPrometheus()
+}
diff --git a/go/sciond/BUILD.bazel b/go/sciond/BUILD.bazel
index 5af5bd109..9475bb728 100644
--- a/go/sciond/BUILD.bazel
+++ b/go/sciond/BUILD.bazel
@@ -1,20 +1,28 @@
 load("@io_bazel_rules_go//go:def.bzl", "go_library")
 load("//:scion.bzl", "scion_go_binary")
 
+scion_go_binary(
+    name = "sciond",
+    embed = [":go_default_library"],
+    visibility = ["//visibility:public"],
+)
+
 go_library(
     name = "go_default_library",
-    srcs = ["main.go"],
+    srcs = [
+        "help_config.go",
+        "main.go",
+        "version.go",
+    ],
     importpath = "github.com/scionproto/scion/go/sciond",
     visibility = ["//visibility:private"],
     deps = [
-        "//go/lib/common:go_default_library",
         "//go/lib/config:go_default_library",
         "//go/lib/env:go_default_library",
         "//go/lib/fatal:go_default_library",
         "//go/lib/infra/infraenv:go_default_library",
         "//go/lib/infra/messenger/tcp:go_default_library",
         "//go/lib/infra/modules/itopo:go_default_library",
-        "//go/lib/infra/modules/segfetcher:go_default_library",
         "//go/lib/log:go_default_library",
         "//go/lib/pathdb:go_default_library",
         "//go/lib/pathstorage:go_default_library",
@@ -23,20 +31,12 @@ go_library(
         "//go/lib/revcache:go_default_library",
         "//go/lib/serrors:go_default_library",
         "//go/lib/topology:go_default_library",
+        "//go/pkg/sciond:go_default_library",
+        "//go/pkg/sciond/config:go_default_library",
+        "//go/pkg/sciond/fetcher:go_default_library",
         "//go/pkg/trust:go_default_library",
         "//go/pkg/trust/compat:go_default_library",
         "//go/pkg/trust/metrics:go_default_library",
-        "//go/proto:go_default_library",
-        "//go/sciond/config:go_default_library",
-        "//go/sciond/internal/fetcher:go_default_library",
-        "//go/sciond/internal/servers:go_default_library",
-        "@com_github_burntsushi_toml//:go_default_library",
-        "@com_github_opentracing_opentracing_go//:go_default_library",
+        "@com_github_spf13_cobra//:go_default_library",
     ],
 )
-
-scion_go_binary(
-    name = "sciond",
-    embed = [":go_default_library"],
-    visibility = ["//visibility:public"],
-)
diff --git a/go/sciond/help_config.go b/go/sciond/help_config.go
new file mode 100644
index 000000000..480cd35ce
--- /dev/null
+++ b/go/sciond/help_config.go
@@ -0,0 +1,34 @@
+// Copyright 2020 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 (
+	"os"
+
+	"github.com/spf13/cobra"
+
+	"github.com/scionproto/scion/go/pkg/sciond/config"
+)
+
+func newHelpConfig() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "help-config",
+		Short: "Display sample configuration file",
+		Run: func(cmd *cobra.Command, args []string) {
+			(&config.Config{}).Sample(os.Stdout, nil, nil)
+		},
+	}
+	return cmd
+}
diff --git a/go/sciond/internal/fetcher/mock_fetcher/fetcher.go b/go/sciond/internal/fetcher/mock_fetcher/fetcher.go
deleted file mode 100644
index 17c50d6a0..000000000
--- a/go/sciond/internal/fetcher/mock_fetcher/fetcher.go
+++ /dev/null
@@ -1,48 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: github.com/scionproto/scion/go/sciond/internal/fetcher (interfaces: Policy)
-
-// Package mock_fetcher is a generated GoMock package.
-package mock_fetcher
-
-import (
-	gomock "github.com/golang/mock/gomock"
-	pathpol "github.com/scionproto/scion/go/lib/pathpol"
-	reflect "reflect"
-)
-
-// MockPolicy is a mock of Policy interface
-type MockPolicy struct {
-	ctrl     *gomock.Controller
-	recorder *MockPolicyMockRecorder
-}
-
-// MockPolicyMockRecorder is the mock recorder for MockPolicy
-type MockPolicyMockRecorder struct {
-	mock *MockPolicy
-}
-
-// NewMockPolicy creates a new mock instance
-func NewMockPolicy(ctrl *gomock.Controller) *MockPolicy {
-	mock := &MockPolicy{ctrl: ctrl}
-	mock.recorder = &MockPolicyMockRecorder{mock}
-	return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use
-func (m *MockPolicy) EXPECT() *MockPolicyMockRecorder {
-	return m.recorder
-}
-
-// Filter mocks base method
-func (m *MockPolicy) Filter(arg0 pathpol.PathSet) pathpol.PathSet {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "Filter", arg0)
-	ret0, _ := ret[0].(pathpol.PathSet)
-	return ret0
-}
-
-// Filter indicates an expected call of Filter
-func (mr *MockPolicyMockRecorder) Filter(arg0 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Filter", reflect.TypeOf((*MockPolicy)(nil).Filter), arg0)
-}
diff --git a/go/sciond/main.go b/go/sciond/main.go
index cab6d4445..eaffb3341 100644
--- a/go/sciond/main.go
+++ b/go/sciond/main.go
@@ -1,4 +1,4 @@
-// Copyright 2018 ETH Zurich, Anapaya Systems
+// Copyright 2020 Anapaya Systems
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -15,28 +15,20 @@
 package main
 
 import (
-	"bytes"
 	"context"
-	"flag"
 	"fmt"
-	"net"
-	"net/http"
 	_ "net/http/pprof"
 	"os"
-	"path/filepath"
 	"time"
 
-	"github.com/BurntSushi/toml"
-	"github.com/opentracing/opentracing-go"
+	"github.com/spf13/cobra"
 
-	"github.com/scionproto/scion/go/lib/common"
 	libconfig "github.com/scionproto/scion/go/lib/config"
 	"github.com/scionproto/scion/go/lib/env"
 	"github.com/scionproto/scion/go/lib/fatal"
 	"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/itopo"
-	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher"
 	"github.com/scionproto/scion/go/lib/log"
 	"github.com/scionproto/scion/go/lib/pathdb"
 	"github.com/scionproto/scion/go/lib/pathstorage"
@@ -45,213 +37,153 @@ import (
 	"github.com/scionproto/scion/go/lib/revcache"
 	"github.com/scionproto/scion/go/lib/serrors"
 	"github.com/scionproto/scion/go/lib/topology"
+	"github.com/scionproto/scion/go/pkg/sciond"
+	"github.com/scionproto/scion/go/pkg/sciond/config"
+	"github.com/scionproto/scion/go/pkg/sciond/fetcher"
 	"github.com/scionproto/scion/go/pkg/trust"
 	"github.com/scionproto/scion/go/pkg/trust/compat"
 	trustmetrics "github.com/scionproto/scion/go/pkg/trust/metrics"
-	"github.com/scionproto/scion/go/proto"
-	"github.com/scionproto/scion/go/sciond/config"
-	"github.com/scionproto/scion/go/sciond/internal/fetcher"
-	"github.com/scionproto/scion/go/sciond/internal/servers"
 )
 
-const (
-	ShutdownWaitTimeout = 5 * time.Second
-)
+func main() {
+	var flags struct {
+		config string
+	}
 
-var (
-	cfg config.Config
-)
+	root := &cobra.Command{
+		Use:           "sciond",
+		Short:         "SCION Daemon",
+		SilenceErrors: true,
+		Args:          cobra.NoArgs,
+		RunE: func(cmd *cobra.Command, args []string) error {
+			return run(flags.config)
+		},
+	}
+	root.AddCommand(
+		newVersion(),
+		newHelpConfig(),
+	)
+	root.Flags().StringVar(&flags.config, "config", "", "Configuration file (required)")
+	root.MarkFlagRequired("config")
 
-func init() {
-	flag.Usage = env.Usage
+	if err := root.Execute(); err != nil {
+		fmt.Fprintf(os.Stderr, "Error: %s\n", err)
+		os.Exit(1)
+	}
 }
 
-func main() {
-	os.Exit(realMain())
-}
+const (
+	shutdownWaitTimeout = 5 * time.Second
+)
 
-func realMain() int {
+func run(file string) error {
 	fatal.Init()
-	env.AddFlags()
-	flag.Parse()
-	if v, ok := env.CheckFlags(&cfg); !ok {
-		return v
-	}
-	if err := setupBasic(); err != nil {
-		fmt.Fprintln(os.Stderr, err)
-		return 1
+	cfg, err := setupBasic(file)
+	if err != nil {
+		return err
 	}
 	defer log.Flush()
 	defer env.LogAppStopped("SD", cfg.General.ID)
 	defer log.HandlePanic()
-	if err := setup(); err != nil {
-		log.Crit("Setup failed", "err", err)
-		return 1
-	}
-	pathDB, revCache, err := pathstorage.NewPathStorage(cfg.PathDB)
-	if err != nil {
-		log.Crit("Unable to initialize path storage", "err", err)
-		return 1
+	if err := setup(cfg); err != nil {
+		return err
 	}
-	defer pathDB.Close()
-	defer revCache.Close()
-	tracer, trCloser, err := cfg.Tracing.NewTracer(cfg.General.ID)
+
+	closer, err := sciond.InitTracer(cfg)
 	if err != nil {
-		log.Crit("Unable to create tracer", "err", err)
-		return 1
+		return serrors.WrapStr("initializing tracer", err)
 	}
-	defer trCloser.Close()
-	opentracing.SetGlobalTracer(tracer)
+	defer closer.Close()
 
-	publicIP, err := net.ResolveUDPAddr("udp", cfg.SD.Address)
+	pathDB, revCache, err := pathstorage.NewPathStorage(cfg.PathDB)
 	if err != nil {
-		log.Crit("Unable to resolve listening address", "err", err, "addr", publicIP)
-		return 1
+		return serrors.WrapStr("initializing path storage", err)
 	}
+	defer pathDB.Close()
+	defer revCache.Close()
+	cleaner := periodic.Start(pathdb.NewCleaner(pathDB, "sd_segments"),
+		300*time.Second, 295*time.Second)
+	defer cleaner.Stop()
+	rcCleaner := periodic.Start(revcache.NewCleaner(revCache, "sd_revocation"),
+		10*time.Second, 10*time.Second)
+	defer rcCleaner.Stop()
 
-	msgr := tcp.NewClientMessenger()
 	trustDB, err := cfg.TrustDB.New()
 	if err != nil {
-		log.Crit("Error initializing trust database", "err", err)
-		return 1
+		return serrors.WrapStr("initializing trust database", err)
 	}
 	trustDB = trustmetrics.WrapDB(string(cfg.TrustDB.Backend()), trustDB)
 	defer trustDB.Close()
-
-	certsDir := filepath.Join(cfg.General.ConfigDir, "certs")
-	loaded, err := trust.LoadTRCs(context.Background(), certsDir, trustDB)
-	if err != nil {
-		log.Crit("Error loading TRCs from disk", "err", err)
-		return 1
-	}
-	log.Info("TRCs loaded", "files", loaded.Loaded)
-	for f, r := range loaded.Ignored {
-		log.Warn("Ignoring non-TRC", "file", f, "reason", r)
-	}
-
-	loaded, err = trust.LoadChains(context.Background(), certsDir, trustDB)
+	engine, err := sciond.TrustEngine(cfg.General.ConfigDir, trustDB)
 	if err != nil {
-		log.Crit("Error loading certificate chains from disk", "err", err)
-		return 1
-	}
-	log.Info("Certificate chains loaded", "files", loaded.Loaded)
-	for f, r := range loaded.Ignored {
-		log.Warn("Ignoring non-certificate chain", "file", f, "reason", r)
-	}
-
-	engine := trust.Engine{
-		Inspector: trust.DBInspector{DB: trustDB},
-		Provider: trust.FetchingProvider{
-			DB: trustDB,
-			Fetcher: trust.DefaultFetcher{
-				RPC: msgr,
-				IA:  itopo.Get().IA(),
-			},
-			Recurser: trust.LocalOnlyRecurser{},
-			Router:   trust.LocalRouter{IA: itopo.Get().IA()},
-		},
-		DB: trustDB,
-	}
+		return serrors.WrapStr("creating trust engine", err)
+	}
+
+	srv := sciond.Server(cfg.SD.Address, sciond.ServerCfg{
+		Fetcher: fetcher.NewFetcher(
+			tcp.NewClientMessenger(),
+			pathDB,
+			engine,
+			compat.Verifier{Verifier: trust.Verifier{Engine: engine}},
+			revCache,
+			cfg.SD,
+			itopo.Provider(),
+		),
+		Engine:   engine,
+		PathDB:   pathDB,
+		RevCache: revCache,
+	})
+	go func() {
+		defer log.HandlePanic()
+		if err := srv.ListenAndServe(); err != nil {
+			fatal.Fatal(serrors.WrapStr("serving API", err, "addr", cfg.SD.Address))
+		}
+	}()
+	defer func() {
+		ctx, cancel := context.WithTimeout(context.Background(), shutdownWaitTimeout)
+		defer cancel()
+		srv.Shutdown(ctx)
+	}()
 
-	// Route messages to their correct handlers
-	handlers := servers.HandlerMap{
-		proto.SCIONDMsg_Which_pathReq: &servers.PathRequestHandler{
-			Fetcher: fetcher.NewFetcher(
-				msgr,
-				pathDB,
-				engine,
-				compat.Verifier{Verifier: trust.Verifier{Engine: engine}},
-				revCache,
-				cfg.SD,
-				itopo.Provider(),
-			),
-		},
-		proto.SCIONDMsg_Which_asInfoReq: &servers.ASInfoRequestHandler{
-			ASInspector: engine,
-		},
-		proto.SCIONDMsg_Which_ifInfoRequest:      &servers.IFInfoRequestHandler{},
-		proto.SCIONDMsg_Which_serviceInfoRequest: &servers.SVCInfoRequestHandler{},
-		proto.SCIONDMsg_Which_revNotification: &servers.RevNotificationHandler{
-			RevCache:         revCache,
-			Verifier:         compat.Verifier{Verifier: trust.Verifier{Engine: engine}},
-			NextQueryCleaner: segfetcher.NextQueryCleaner{PathDB: pathDB},
-		},
-	}
-	cleaner := periodic.Start(pathdb.NewCleaner(pathDB, "sd_segments"),
-		300*time.Second, 295*time.Second)
-	defer cleaner.Stop()
-	rcCleaner := periodic.Start(revcache.NewCleaner(revCache, "sd_revocation"),
-		10*time.Second, 10*time.Second)
-	defer rcCleaner.Stop()
-	apiServer, shutdownF := NewServer("tcp", cfg.SD.Address, handlers)
-	defer shutdownF()
-	StartServer(cfg.SD.Address, apiServer)
-	http.HandleFunc("/config", configHandler)
-	http.HandleFunc("/info", env.InfoHandler)
-	http.HandleFunc("/topology", itopo.TopologyHandler)
-	cfg.Metrics.StartPrometheus()
+	sciond.StartHTTPEndpoints(cfg, cfg.Metrics)
 	select {
 	case <-fatal.ShutdownChan():
 		// Whenever we receive a SIGINT or SIGTERM we exit without an error.
 		// Deferred shutdowns for all running servers run now.
-		return 0
+		return nil
 	case <-fatal.FatalChan():
-		return 1
+		return serrors.New("shutdown on error")
 	}
 }
 
-func setupBasic() error {
-	if err := libconfig.LoadFile(env.ConfigFile(), &cfg); err != nil {
-		return serrors.WrapStr("failed to load config", err, "file", env.ConfigFile())
+func setupBasic(file string) (config.Config, error) {
+	var cfg config.Config
+	if err := libconfig.LoadFile(file, &cfg); err != nil {
+		return config.Config{}, serrors.WrapStr("loading config from file", err, "file", file)
 	}
 	cfg.InitDefaults()
 	if err := log.Setup(cfg.Logging); err != nil {
-		return serrors.WrapStr("failed to initialize logging", err)
+		return config.Config{}, serrors.WrapStr("initialize logging", err)
 	}
 	prom.ExportElementID(cfg.General.ID)
-	return env.LogAppStarted("SD", cfg.General.ID)
+	if err := env.LogAppStarted("SD", cfg.General.ID); err != nil {
+		return config.Config{}, err
+	}
+	return cfg, nil
 }
 
-func setup() error {
+func setup(cfg config.Config) error {
 	if err := cfg.Validate(); err != nil {
-		return common.NewBasicError("unable to validate config", err)
+		return serrors.WrapStr("validating config", err)
 	}
 	topo, err := topology.FromJSONFile(cfg.General.Topology())
 	if err != nil {
-		return common.NewBasicError("unable to load topology", err)
+		return serrors.WrapStr("loading topology", err)
 	}
 	itopo.Init(&itopo.Config{})
 	if err := itopo.Update(topo); err != nil {
-		return common.NewBasicError("unable to set initial static topology", err)
+		return serrors.WrapStr("unable to set initial static topology", err)
 	}
 	infraenv.InitInfraEnvironment(cfg.General.Topology())
 	return nil
 }
-
-func NewServer(network string, rsockPath string,
-	handlers servers.HandlerMap) (*servers.Server, func()) {
-
-	server := servers.NewServer(network, rsockPath, handlers)
-	shutdownF := func() {
-		ctx, cancelF := context.WithTimeout(context.Background(), ShutdownWaitTimeout)
-		server.Shutdown(ctx)
-		cancelF()
-	}
-	return server, shutdownF
-}
-
-func StartServer(address string, server *servers.Server) {
-	go func() {
-		defer log.HandlePanic()
-		if err := server.ListenAndServe(); err != nil {
-			fatal.Fatal(common.NewBasicError("ListenAndServe error", err, "address", address))
-		}
-	}()
-}
-
-func configHandler(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "text/plain")
-	var buf bytes.Buffer
-	toml.NewEncoder(&buf).Encode(cfg)
-	fmt.Fprint(w, buf.String())
-}
diff --git a/go/sciond/version.go b/go/sciond/version.go
new file mode 100644
index 000000000..6887e0b9f
--- /dev/null
+++ b/go/sciond/version.go
@@ -0,0 +1,34 @@
+// Copyright 2020 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 (
+	"fmt"
+
+	"github.com/spf13/cobra"
+
+	"github.com/scionproto/scion/go/lib/env"
+)
+
+func newVersion() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "version",
+		Short: "Show the SCION version information",
+		Run: func(cmd *cobra.Command, args []string) {
+			fmt.Println(env.VersionInfo())
+		},
+	}
+	return cmd
+}
diff --git a/python/topology/supervisor.py b/python/topology/supervisor.py
index b8198698e..21977b823 100755
--- a/python/topology/supervisor.py
+++ b/python/topology/supervisor.py
@@ -64,7 +64,7 @@ class SupervisorGenerator(object):
         entries = []
         for k, v in topo.get("border_routers", {}).items():
             conf = os.path.join(base, k, BR_CONFIG_NAME)
-            entries.append((k, [cmd, "-config", conf]))
+            entries.append((k, [cmd, "--config", conf]))
         return entries
 
     def _control_service_entries(self, topo, base):
@@ -73,12 +73,12 @@ class SupervisorGenerator(object):
             # only a single control service instance per AS is currently supported
             if k.endswith("-1"):
                 conf = os.path.join(base, k, CS_CONFIG_NAME)
-                entries.append((k, ["bin/cs", "-config", conf]))
+                entries.append((k, ["bin/cs", "--config", conf]))
         return entries
 
     def _sciond_entry(self, name, conf_dir):
         return self._common_entry(
-            name, ["bin/sciond", "-config", os.path.join(conf_dir, SD_CONFIG_NAME)])
+            name, ["bin/sciond", "--config", os.path.join(conf_dir, SD_CONFIG_NAME)])
 
     def _write_as_conf(self, topo_id, entries):
         config = configparser.ConfigParser(interpolation=None)
@@ -115,7 +115,7 @@ class SupervisorGenerator(object):
         elem_dir = os.path.join(self.args.output_dir, elem)
         config_file_path = os.path.join(elem_dir, DISP_CONFIG_NAME)
         self._write_elem_conf(
-            elem, ["bin/dispatcher", "-config", config_file_path], elem_dir)
+            elem, ["bin/dispatcher", "--config", config_file_path], elem_dir)
 
     def _common_entry(self, name, cmd_args, elem_dir=None):
         entry = {
diff --git a/tools/gomocks b/tools/gomocks
index 37b9687ec..3293cd858 100755
--- a/tools/gomocks
+++ b/tools/gomocks
@@ -34,6 +34,7 @@ MOCK_TARGETS = [
     ("net", "Addr,Conn,PacketConn"),
     (SCION_PACKAGE_PREFIX + "/go/pkg/cs/trust/handler", "ChainBuilder,RenewalRequestVerifier"),
     (SCION_PACKAGE_PREFIX + "/go/pkg/cs/trust", "CACertProvider,PolicyGen,SignerGen"),
+    (SCION_PACKAGE_PREFIX + "/go/pkg/sciond/fetcher", "Fetcher,Policy"),
     (SCION_PACKAGE_PREFIX + "/go/pkg/trust", "DB,Fetcher,Inspector,KeyRing,Provider," +
         "Recurser,Router,RPC"),
     (SCION_PACKAGE_PREFIX + "/go/pkg/trust/renewal", "DB"),
@@ -68,7 +69,6 @@ MOCK_TARGETS = [
     (SCION_PACKAGE_PREFIX + "/go/lib/underlay/conn", "Conn"),
     (SCION_PACKAGE_PREFIX + "/go/lib/periodic/internal/metrics", "ExportMetric"),
     (SCION_PACKAGE_PREFIX + "/go/lib/xtest", "Callback"),
-    (SCION_PACKAGE_PREFIX + "/go/sciond/internal/fetcher", "Policy"),
     (SCION_PACKAGE_PREFIX + "/go/sig/egress/iface", "Session"),
     (SCION_PACKAGE_PREFIX + "/go/sig/egress/worker", "SCIONWriter"),
 ]


Test patch:
diff --git a/go/sciond/internal/fetcher/filter.go b/go/pkg/sciond/fetcher/filter.go
similarity index 100%
rename from go/sciond/internal/fetcher/filter.go
rename to go/pkg/sciond/fetcher/filter.go
diff --git a/go/sciond/internal/fetcher/filter_test.go b/go/pkg/sciond/fetcher/filter_test.go
similarity index 95%
rename from go/sciond/internal/fetcher/filter_test.go
rename to go/pkg/sciond/fetcher/filter_test.go
index aa36b2465..5f4004bd5 100644
--- a/go/sciond/internal/fetcher/filter_test.go
+++ b/go/pkg/sciond/fetcher/filter_test.go
@@ -27,8 +27,8 @@ import (
 	"github.com/scionproto/scion/go/lib/pathpol"
 	"github.com/scionproto/scion/go/lib/xtest"
 	"github.com/scionproto/scion/go/lib/xtest/graph"
-	"github.com/scionproto/scion/go/sciond/internal/fetcher"
-	"github.com/scionproto/scion/go/sciond/internal/fetcher/mock_fetcher"
+	"github.com/scionproto/scion/go/pkg/sciond/fetcher"
+	"github.com/scionproto/scion/go/pkg/sciond/fetcher/mock_fetcher"
 )
 
 func TestFilter(t *testing.T) {
diff --git a/go/sciond/internal/metrics/metrics.go b/go/pkg/sciond/internal/metrics/metrics.go
similarity index 100%
rename from go/sciond/internal/metrics/metrics.go
rename to go/pkg/sciond/internal/metrics/metrics.go
diff --git a/go/sciond/internal/metrics/metrics_test.go b/go/pkg/sciond/internal/metrics/metrics_test.go
similarity index 93%
rename from go/sciond/internal/metrics/metrics_test.go
rename to go/pkg/sciond/internal/metrics/metrics_test.go
index 3653f5a06..d091f9587 100644
--- a/go/sciond/internal/metrics/metrics_test.go
+++ b/go/pkg/sciond/internal/metrics/metrics_test.go
@@ -18,7 +18,7 @@ import (
 	"testing"
 
 	"github.com/scionproto/scion/go/lib/prom/promtest"
-	"github.com/scionproto/scion/go/sciond/internal/metrics"
+	"github.com/scionproto/scion/go/pkg/sciond/internal/metrics"
 )
 
 func TestLabels(t *testing.T) {